debounced telemetry event on command search term

This commit is contained in:
Francesco Sansalvadore committed 2025-10-22 12:02:24 +02:00
1 parent 4ec3ff7506
commit f7d746fd32
3 files changed
+95 -5

No files matched your search

@@ -217,7 +217,7 @@ const LayoutHeader = ({
<div className="flex items-center gap-x-2">
{customHeaderComponents && customHeaderComponents}
<CommandMenuTriggerInput
placeholder="Find..."
placeholder="Search..."
className="
hidden md:flex
border-transparent lg:border-default
@@ -233,7 +233,6 @@ const LayoutHeader = ({
{IS_PLATFORM ? (
<>
<FeedbackDropdown />
<div className="overflow-hidden flex items-center rounded-full border">
<HelpPopover />
<NotificationsPopoverV2 />
+24 -1
View File
@@ -1964,7 +1964,29 @@ export interface CommandMenuOpenedEvent {
*/
trigger: 'keyboard_shortcut' | 'search_input'
/**
* The trigger that opened the command menu
* In which app the command input was typed
*/
app: 'studio' | 'docs' | 'www'
}
groups: TelemetryGroups
}
/**
* User typed a search term in the command menu input.
*
* @group Events
* @source ui-patterns
* @page any
*/
export interface CommandInputTypedEvent {
action: 'command_input_typed'
properties: {
/**
* Search term typed into the command menu input
*/
value: string
/**
* In which app the command input was typed
*/
app: 'studio' | 'docs' | 'www'
}
@@ -2086,3 +2108,4 @@ export type TelemetryEvent =
| TableRLSEnabledEvent
| AuthUsersSearchSubmittedEvent
| CommandMenuOpenedEvent
| CommandInputTypedEvent
@@ -1,11 +1,13 @@
'use client'
import React, { forwardRef, useEffect, useRef, useState } from 'react'
import { forwardRef, useCallback, useEffect, useRef, useState } from 'react'
import type React from 'react'
import { useBreakpoint } from 'common'
import { CommandInput_Shadcn_, cn } from 'ui'
import { useQuery, useSetQuery } from './hooks/queryHooks'
import { useCommandMenuTelemetryContext } from './hooks/useCommandMenuTelemetryContext'
function useFocusInputOnWiderScreens(ref: React.ForwardedRef<HTMLInputElement>) {
const isBelowSm = useBreakpoint('sm')
@@ -50,8 +52,74 @@ const CommandInput = forwardRef<
const [inputValue, setInputValue] = useState(query)
useEffect(() => {
setInputValue(query)
previousValueRef.current = query
}, [query])
// Get telemetry context
const telemetryContext = useCommandMenuTelemetryContext()
// Debounced telemetry tracking
const debounceTimeoutRef = useRef<NodeJS.Timeout>()
const previousValueRef = useRef<string>(inputValue)
const logTelemetryEvent = useCallback(
(value: string) => {
if (telemetryContext?.onTelemetry) {
// Create a CommandInputTypedEvent
const event = {
action: 'command_input_typed' as const,
properties: {
value: value,
app: telemetryContext.app,
},
groups: {} as Record<string, string>, // Groups will be populated by the telemetry system
}
// Cast to unknown first, then to the expected type to bypass type checking
// This is a workaround for the type mismatch between CommandInputTypedEvent and CommandMenuOpenedEvent
telemetryContext.onTelemetry(
event as unknown as Parameters<typeof telemetryContext.onTelemetry>[0]
)
}
},
[telemetryContext]
)
const debouncedLogTelemetry = useCallback(
(value: string) => {
// Only trigger telemetry if the user is adding characters (not removing with backspace)
const isAddingCharacters = value.length > previousValueRef.current.length
if (!isAddingCharacters) {
previousValueRef.current = value
return
}
if (debounceTimeoutRef.current) {
clearTimeout(debounceTimeoutRef.current)
}
debounceTimeoutRef.current = setTimeout(() => {
logTelemetryEvent(value)
previousValueRef.current = value
}, 500)
},
[logTelemetryEvent]
)
// Cleanup timeout on unmount
useEffect(() => {
return () => {
if (debounceTimeoutRef.current) {
clearTimeout(debounceTimeoutRef.current)
}
}
}, [])
const handleValueChange = (value: string) => {
setInputValue(value)
debouncedLogTelemetry(value)
}
// To handle CJK input
const [imeComposing, setImeComposing] = useState(false)
useEffect(() => {
@@ -67,7 +135,7 @@ const CommandInput = forwardRef<
autoFocus={false}
ref={inputRef}
value={inputValue}
onValueChange={setInputValue}
onValueChange={handleValueChange}
placeholder="Run a command or search..."
onCompositionStart={() => setImeComposing(true)}
onCompositionEnd={() => setImeComposing(false)}