Skip to content

Commit af43adb

Browse files
chore: command events (supabase#39775)
* add CommandMenuTriggerInput in ui-patterns * optimize search input responsiveness * add event on commandMenu opened * add event on debounced commandMenu input submission * add event on commandMenu command selection * add commandMenu telemetry to studio, www and docs --------- Co-authored-by: Charis <[email protected]>
1 parent 416df4a commit af43adb

File tree

21 files changed

+544
-90
lines changed

21 files changed

+544
-90
lines changed

apps/docs/components/Navigation/NavigationMenu/TopNavBar.tsx

Lines changed: 9 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { memo, useState } from 'react'
99
import { useIsLoggedIn, useIsUserLoading, useUser } from 'common'
1010
import { isFeatureEnabled } from 'common/enabled-features'
1111
import { Button, buttonVariants, cn } from 'ui'
12-
import { AuthenticatedDropdownMenu, CommandMenuTrigger } from 'ui-patterns'
12+
import { AuthenticatedDropdownMenu, CommandMenuTriggerInput } from 'ui-patterns'
1313
import { getCustomContent } from '../../../lib/custom-content/getCustomContent'
1414
import GlobalNavigationMenu from './GlobalNavigationMenu'
1515
import useDropdownMenu from './useDropdownMenu'
@@ -43,37 +43,14 @@ const TopNavBar: FC = () => {
4343
</div>
4444

4545
<div className="flex gap-2 items-center">
46-
<CommandMenuTrigger>
47-
<button
48-
className={cn(
49-
'group',
50-
'flex-grow md:w-44 xl:w-56 h-[30px] rounded-md',
51-
'pl-1.5 md:pl-2 pr-1',
52-
'flex items-center justify-between',
53-
'bg-surface-100/75 text-foreground-lighter border',
54-
'hover:bg-opacity-100 hover:border-strong',
55-
'focus-visible:!outline-4 focus-visible:outline-offset-1 focus-visible:outline-brand-600',
56-
'transition'
57-
)}
58-
>
59-
<div className="flex items-center space-x-2 text-foreground-muted">
60-
<Search size={18} strokeWidth={2} />
61-
<p className="flex text-sm pr-2">
62-
Search
63-
<span className="hidden xl:inline ml-1"> docs...</span>
64-
</p>
65-
</div>
66-
<div className="hidden md:flex items-center space-x-1">
67-
<div
68-
aria-hidden="true"
69-
className="md:flex items-center justify-center h-full px-1 border rounded bg-surface-300 gap-0.5"
70-
>
71-
<Command size={12} strokeWidth={1.5} />
72-
<span className="text-[12px]">K</span>
73-
</div>
74-
</div>
75-
</button>
76-
</CommandMenuTrigger>
46+
<CommandMenuTriggerInput
47+
placeholder={
48+
<>
49+
Search
50+
<span className="hidden xl:inline ml-1"> docs...</span>
51+
</>
52+
}
53+
/>
7754
<button
7855
title="Menu dropdown button"
7956
className={cn(

apps/docs/features/app.providers.tsx

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
1-
import { type PropsWithChildren } from 'react'
1+
import type { PropsWithChildren } from 'react'
22

33
import { FeatureFlagProvider, IS_PLATFORM, ThemeProvider } from 'common'
44
import { SonnerToaster, TooltipProvider } from 'ui'
5-
import { CommandProvider } from 'ui-patterns/CommandMenu'
65
import SiteLayout from '~/layouts/SiteLayout'
76
import { API_URL } from '~/lib/constants'
87
import { AuthContainer } from './auth/auth.client'
9-
import { DocsCommandMenu } from './command'
8+
import { DocsCommandMenu, DocsCommandProvider } from './command'
109
import { QueryClientProvider } from './data/queryClient.client'
1110
import { PageTelemetry } from './telemetry/telemetry.client'
1211
import { ScrollRestoration } from './ui/helpers.scroll.client'
@@ -24,15 +23,15 @@ function GlobalProviders({ children }: PropsWithChildren) {
2423
<ScrollRestoration />
2524
<ThemeProvider defaultTheme="system" enableSystem disableTransitionOnChange>
2625
<TooltipProvider delayDuration={0}>
27-
<CommandProvider>
26+
<DocsCommandProvider>
2827
<div className="flex flex-col">
2928
<SiteLayout>
3029
{children}
3130
<DocsCommandMenu />
3231
</SiteLayout>
3332
<ThemeSandbox />
3433
</div>
35-
</CommandProvider>
34+
</DocsCommandProvider>
3635
<SonnerToaster position="top-right" />
3736
</TooltipProvider>
3837
</ThemeProvider>
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
'use client'
2+
3+
import type { PropsWithChildren } from 'react'
4+
5+
import { CommandProvider } from 'ui-patterns/CommandMenu'
6+
import { useDocsCommandMenuTelemetry } from 'hooks/useDocsCommandMenuTelemetry'
7+
8+
export function DocsCommandProvider({ children }: PropsWithChildren) {
9+
const { onTelemetry } = useDocsCommandMenuTelemetry()
10+
11+
return (
12+
<CommandProvider app="docs" onTelemetry={onTelemetry}>
13+
{children}
14+
</CommandProvider>
15+
)
16+
}

apps/docs/features/command/index.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import dynamic from 'next/dynamic'
44

55
import { useCommandMenuInitiated } from 'ui-patterns/CommandMenu'
6+
import { DocsCommandProvider } from './DocsCommandProvider'
67

78
const LazyCommandMenu = dynamic(() => import('./CommandMenu'), { ssr: false })
89

@@ -11,4 +12,4 @@ const DocsCommandMenu = () => {
1112
return isInitiated && <LazyCommandMenu />
1213
}
1314

14-
export { DocsCommandMenu }
15+
export { DocsCommandMenu, DocsCommandProvider }
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
'use client'
2+
3+
import { useCallback } from 'react'
4+
5+
import type {
6+
CommandMenuOpenedEvent,
7+
CommandMenuCommandSelectedEvent,
8+
CommandMenuSearchSubmittedEvent,
9+
} from 'common/telemetry-constants'
10+
import { useSendTelemetryEvent } from 'lib/telemetry'
11+
12+
export function useDocsCommandMenuTelemetry() {
13+
const sendTelemetryEvent = useSendTelemetryEvent()
14+
15+
const onTelemetry = useCallback(
16+
(
17+
event:
18+
| CommandMenuOpenedEvent
19+
| CommandMenuCommandSelectedEvent
20+
| CommandMenuSearchSubmittedEvent
21+
) => {
22+
sendTelemetryEvent(event)
23+
},
24+
[sendTelemetryEvent]
25+
)
26+
27+
return { onTelemetry }
28+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import type { PropsWithChildren } from 'react'
2+
3+
import { CommandProvider } from 'ui-patterns/CommandMenu'
4+
import { useStudioCommandMenuTelemetry } from 'hooks/misc/useStudioCommandMenuTelemetry'
5+
import { useLocalStorageQuery } from 'hooks/misc/useLocalStorage'
6+
import { LOCAL_STORAGE_KEYS } from 'common'
7+
8+
export function StudioCommandProvider({ children }: PropsWithChildren) {
9+
const { onTelemetry } = useStudioCommandMenuTelemetry()
10+
const [commandMenuHotkeyEnabled] = useLocalStorageQuery<boolean>(
11+
LOCAL_STORAGE_KEYS.HOTKEY_COMMAND_MENU,
12+
true
13+
)
14+
15+
return (
16+
<CommandProvider
17+
app="studio"
18+
onTelemetry={onTelemetry}
19+
openKey={commandMenuHotkeyEnabled ? 'k' : ''}
20+
>
21+
{children}
22+
</CommandProvider>
23+
)
24+
}

apps/studio/components/interfaces/UserDropdown.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import {
2323
Theme,
2424
singleThemes,
2525
} from 'ui'
26-
import { useSetCommandMenuOpen } from 'ui-patterns/CommandMenu'
26+
import { useCommandMenuOpenedTelemetry, useSetCommandMenuOpen } from 'ui-patterns/CommandMenu'
2727
import { useFeaturePreviewModal } from './App/FeaturePreview/FeaturePreviewContext'
2828

2929
export function UserDropdown() {
@@ -35,8 +35,14 @@ export function UserDropdown() {
3535

3636
const signOut = useSignOut()
3737
const setCommandMenuOpen = useSetCommandMenuOpen()
38+
const sendTelemetry = useCommandMenuOpenedTelemetry()
3839
const { openFeaturePreviewModal } = useFeaturePreviewModal()
3940

41+
const handleCommandMenuOpen = () => {
42+
setCommandMenuOpen(true)
43+
sendTelemetry()
44+
}
45+
4046
return (
4147
<DropdownMenu>
4248
<DropdownMenuTrigger asChild className="border flex-shrink-0 px-3">
@@ -97,7 +103,7 @@ export function UserDropdown() {
97103
<FlaskConical size={14} strokeWidth={1.5} className="text-foreground-lighter" />
98104
Feature previews
99105
</DropdownMenuItem>
100-
<DropdownMenuItem className="flex gap-2" onClick={() => setCommandMenuOpen(true)}>
106+
<DropdownMenuItem className="flex gap-2" onClick={handleCommandMenuOpen}>
101107
<Command size={14} strokeWidth={1.5} className="text-foreground-lighter" />
102108
Command menu
103109
</DropdownMenuItem>
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { useCallback } from 'react'
2+
3+
import { useParams } from 'common'
4+
import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization'
5+
import { useSendEventMutation } from 'data/telemetry/send-event-mutation'
6+
import type {
7+
CommandMenuOpenedEvent,
8+
CommandMenuCommandSelectedEvent,
9+
CommandMenuSearchSubmittedEvent,
10+
} from 'common/telemetry-constants'
11+
12+
export function useStudioCommandMenuTelemetry() {
13+
const { ref: projectRef } = useParams()
14+
const { data: organization } = useSelectedOrganizationQuery()
15+
const { mutate: sendEvent } = useSendEventMutation()
16+
17+
const onTelemetry = useCallback(
18+
(
19+
event:
20+
| CommandMenuOpenedEvent
21+
| CommandMenuCommandSelectedEvent
22+
| CommandMenuSearchSubmittedEvent
23+
) => {
24+
// Add studio-specific groups (project and organization)
25+
const eventWithGroups = {
26+
...event,
27+
groups: {
28+
...event.groups,
29+
...(projectRef && { project: projectRef }),
30+
...(organization?.slug && { organization: organization.slug }),
31+
},
32+
}
33+
34+
sendEvent(eventWithGroups)
35+
},
36+
[projectRef, organization?.slug, sendEvent]
37+
)
38+
39+
return { onTelemetry }
40+
}

apps/studio/pages/_app.tsx

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,12 @@ import timezone from 'dayjs/plugin/timezone'
3030
import utc from 'dayjs/plugin/utc'
3131
import Head from 'next/head'
3232
import { NuqsAdapter } from 'nuqs/adapters/next/pages'
33-
import { ErrorInfo, PropsWithChildren, useCallback } from 'react'
33+
import { ErrorInfo, useCallback } from 'react'
3434
import { ErrorBoundary } from 'react-error-boundary'
3535

3636
import {
3737
FeatureFlagProvider,
3838
getFlags,
39-
LOCAL_STORAGE_KEYS,
4039
TelemetryTagManager,
4140
ThemeProvider,
4241
useThemeSandbox,
@@ -52,14 +51,13 @@ import { GlobalErrorBoundaryState } from 'components/ui/ErrorBoundary/GlobalErro
5251
import { useRootQueryClient } from 'data/query-client'
5352
import { customFont, sourceCodePro } from 'fonts'
5453
import { useCustomContent } from 'hooks/custom-content/useCustomContent'
55-
import { useLocalStorageQuery } from 'hooks/misc/useLocalStorage'
5654
import { AuthProvider } from 'lib/auth'
5755
import { API_URL, BASE_PATH, IS_PLATFORM, useDefaultProvider } from 'lib/constants'
5856
import { ProfileProvider } from 'lib/profile'
5957
import { Telemetry } from 'lib/telemetry'
6058
import { AppPropsWithLayout } from 'types'
6159
import { SonnerToaster, TooltipProvider } from 'ui'
62-
import { CommandProvider } from 'ui-patterns/CommandMenu'
60+
import { StudioCommandProvider as CommandProvider } from 'components/interfaces/App/CommandMenu/StudioCommandProvider'
6361

6462
dayjs.extend(customParseFormat)
6563
dayjs.extend(utc)
@@ -80,15 +78,6 @@ loader.config({
8078
},
8179
})
8280

83-
const CommandProviderWithPreferences = ({ children }: PropsWithChildren) => {
84-
const [commandMenuHotkeyEnabled] = useLocalStorageQuery<boolean>(
85-
LOCAL_STORAGE_KEYS.HOTKEY_COMMAND_MENU,
86-
true
87-
)
88-
89-
return <CommandProvider openKey={commandMenuHotkeyEnabled ? 'k' : ''}>{children}</CommandProvider>
90-
}
91-
9281
// [Joshen TODO] Once we settle on the new nav layout - we'll need a lot of clean up in terms of our layout components
9382
// a lot of them are unnecessary and introduce way too many cluttered CSS especially with the height styles that make
9483
// debugging way too difficult. Ideal scenario is we just have one AppLayout to control the height and scroll areas of
@@ -161,15 +150,15 @@ function CustomApp({ Component, pageProps }: AppPropsWithLayout) {
161150
disableTransitionOnChange
162151
>
163152
<AppBannerContextProvider>
164-
<CommandProviderWithPreferences>
153+
<CommandProvider>
165154
<FeaturePreviewContextProvider>
166155
{getLayout(<Component {...pageProps} />)}
167156
<StudioCommandMenu />
168157
<FeaturePreviewModal />
169158
</FeaturePreviewContextProvider>
170159
<SonnerToaster position="top-right" />
171160
<MonacoThemeProvider />
172-
</CommandProviderWithPreferences>
161+
</CommandProvider>
173162
</AppBannerContextProvider>
174163
</ThemeProvider>
175164
</RouteValidationWrapper>
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { useCallback } from 'react'
2+
3+
import type {
4+
CommandMenuOpenedEvent,
5+
CommandMenuCommandSelectedEvent,
6+
CommandMenuSearchSubmittedEvent,
7+
} from 'common/telemetry-constants'
8+
import { useSendTelemetryEvent } from 'lib/telemetry'
9+
10+
export function useWwwCommandMenuTelemetry() {
11+
const sendTelemetryEvent = useSendTelemetryEvent()
12+
13+
const onTelemetry = useCallback(
14+
(
15+
event:
16+
| CommandMenuOpenedEvent
17+
| CommandMenuCommandSelectedEvent
18+
| CommandMenuSearchSubmittedEvent
19+
) => {
20+
sendTelemetryEvent(event)
21+
},
22+
[sendTelemetryEvent]
23+
)
24+
25+
return { onTelemetry }
26+
}

0 commit comments

Comments
 (0)