1
0
mirror of synced 2025-12-19 18:10:59 -05:00

AI Search feedback fixes round 3 (#54669)

This commit is contained in:
Evan Bonsignori
2025-03-04 14:34:27 -08:00
committed by GitHub
parent 891e46823f
commit f8dec81785
8 changed files with 99 additions and 23 deletions

View File

@@ -26,7 +26,9 @@ release_notes:
search:
input:
aria_label: Open search overlay
placeholder: Search or ask Copilot
placeholder: Search or ask {{icon}} Copilot
placeholder_no_icon: Search or ask Copilot
shortcut: Type {{icon}} to search
overlay:
input_aria_label: Search or ask Copilot
suggestions_list_aria_label: Search suggestions
@@ -38,7 +40,7 @@ search:
beta_tag: Beta
return_to_search: Return to search
clear_search_query: Clear
view_all_search_results: View all {{length}} results
view_all_search_results: View more results
no_results_found: No results found
ai:
disclaimer: Copilot uses AI. Check for mistakes by reviewing the links in the response.

View File

@@ -26,7 +26,9 @@ release_notes:
search:
input:
aria_label: Open search overlay
placeholder: Search or ask Copilot
placeholder: Search or ask {{icon}} Copilot
placeholder_no_icon: Search or ask Copilot
shortcut: Type {{icon}} to search
overlay:
input_aria_label: Search or ask Copilot
suggestions_list_aria_label: Search suggestions
@@ -38,7 +40,7 @@ search:
beta_tag: Beta
return_to_search: Return to search
clear_search_query: Clear
view_all_search_results: View all {{length}} results
view_all_search_results: View more results
no_results_found: No results found
ai:
disclaimer: Copilot uses AI. Check for mistakes by reviewing the links in the response.

View File

@@ -7,6 +7,7 @@ export type MarkdownContentPropsT = {
children: string
className?: string
openLinksInNewTab?: boolean
includeQueryParams?: boolean
eventGroupKey?: string
eventGroupId?: string
as?: keyof JSX.IntrinsicElements
@@ -20,16 +21,48 @@ export const UnrenderedMarkdownContent = ({
children,
className,
openLinksInNewTab = true,
includeQueryParams = true,
eventGroupKey = '',
eventGroupId = '',
...restProps
}: MarkdownContentPropsT) => {
// Overrides for ReactMarkdown components
const components = {} as Components
if (openLinksInNewTab) {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
components.a = ({ node, ...props }) => (
<a {...props} target="_blank" data-group-key={eventGroupKey} data-group-id={eventGroupId}>
components.a = ({ node, ...props }) => {
let href = props.href || ''
let existingAnchorParams = ''
// When we want to include specific query parameters in the URL
if (includeQueryParams) {
if (href.includes('?')) {
href = href.split('?')[0]
existingAnchorParams = href.split('?')[1]
}
// Include feature, search-overlay-ask-ai, and search-overlay-input query parameters if they exist in the current URL
const existingURLParams = new URLSearchParams(window.location.search)
const newParams = new URLSearchParams()
if (existingURLParams.get('feature')) {
newParams.set('feature', existingURLParams.get('feature') || '')
}
if (existingURLParams.get('search-overlay-ask-ai')) {
newParams.set('search-overlay-ask-ai', existingURLParams.get('search-overlay-ask-ai') || '')
}
if (existingURLParams.get('search-overlay-input')) {
newParams.set('search-overlay-input', existingURLParams.get('search-overlay-input') || '')
}
// Combine new and existing query parameters
if (newParams.toString()) {
href = `${href}?${existingAnchorParams}&${newParams.toString()}`
}
}
return (
<a
{...props}
href={href}
target={openLinksInNewTab ? '_blank' : undefined}
data-group-key={eventGroupKey}
data-group-id={eventGroupId}
>
{props.children}
</a>
)

View File

@@ -26,7 +26,7 @@ type UseCombinedSearchReturn = {
clearAutocompleteResults: () => void
}
const DEBOUNCE_TIME = 300 // In milliseconds
const DEBOUNCE_TIME = 1 // In milliseconds
// Results are only cached for the current session
// We cache results so if a user presses backspace, we can show the results immediately without burdening the API
@@ -65,7 +65,7 @@ export function useCombinedSearchResults({
useEffect(() => {
debouncedFetchRef.current = debounce((value: string) => {
fetchAutocompleteResults(value)
}, DEBOUNCE_TIME) // 300ms debounce
}, DEBOUNCE_TIME) // 1ms debounce
return () => {
debouncedFetchRef.current?.cancel()

View File

@@ -223,7 +223,7 @@ export function AskAIResults({
</div>
) : (
<article aria-busy={responseLoading} aria-live="polite">
{!aiCouldNotAnswer && message === '' ? (
{!aiCouldNotAnswer && message !== '' ? (
<span ref={disclaimerRef} className={styles.disclaimerText}>
{t('search.ai.disclaimer')}
</span>

View File

@@ -117,6 +117,34 @@
font-weight: var(--base-text-weight-normal, 400) !important;
}
.placeholderShortcutContainer {
float: right;
color: var(--fgColor-disabled);
margin-right: 8px;
}
.forwardSlashIcon {
display: inline-grid;
width: var(--base-size-16, 16px);
height: var(--base-size-16, 16px);
color: var(--color-fg-default);
margin-right: 4px;
margin-left: 4px;
padding-left: 6px;
padding-right: 6px;
padding-top: 2px;
padding-bottom: 2px;
border-radius: var(--borderRadius-small);
border: 0.0625rem solid var(--color-fg-muted);
border-radius: 0.1875rem;
box-shadow: none;
align-items: center;
justify-content: center;
}
.searchIconContainer {
display: flex;
align-items: center;

View File

@@ -40,6 +40,18 @@ export function SearchBarButton({ isSearchOpen, setIsSearchOpen }: Props) {
}
}
const shortcutElements = t('search.input.shortcut')
.split(/({{[^}]+}})/)
.filter((item) => item.trim() !== '')
.map((item) => <>{item.trim()}</>)
shortcutElements[1] = <kbd className={styles.forwardSlashIcon}>/</kbd>
const placeHolderElements = t('search.input.placeholder')
.split(/({{[^}]+}})/)
.filter((item) => item.trim() !== '')
.map((item) => <>{item.trim()}</>)
placeHolderElements[1] = <CopilotIcon aria-hidden className="mr-1 ml-1" />
return (
<>
{/* We don't want to show the input when overlay is open */}
@@ -78,8 +90,8 @@ export function SearchBarButton({ isSearchOpen, setIsSearchOpen }: Props) {
urlSearchInputQuery
) : (
<>
{t('search.input.placeholder')}
<CopilotIcon aria-hidden className="mr-1 ml-1" />
<span className={styles.placeholderText}>{placeHolderElements}</span>
<span className={styles.placeholderShortcutContainer}>{shortcutElements}</span>
</>
)}
</span>

View File

@@ -133,10 +133,7 @@ export function SearchOverlay({
if (generalSearchResults.length > 0) {
generalOptionsWithViewStatus.push({
title: t('search.overlay.view_all_search_results').replace(
'{{length}}',
totalGeneralSearchResults.toLocaleString('en-US'),
),
title: t('search.overlay.view_all_search_results'),
isViewAllResults: true,
} as any)
} else if (urlSearchInputQuery.trim() !== '' && !searchLoading) {
@@ -468,7 +465,7 @@ export function SearchOverlay({
{/* Always show the AI Search UI error message when it is needed */}
{aiSearchError && (
<>
<ActionList.Divider key="general-divider" />
<ActionList.Divider key="error-top-divider" />
<ActionList.GroupHeading
as="h3"
tabIndex={-1}
@@ -492,7 +489,7 @@ export function SearchOverlay({
role="alert"
/>
</Box>
<ActionList.Divider key="general-divider" />
<ActionList.Divider key="error-bottom-divider" />
</>
)}
{/* Only show the autocomplete search UI error message in Dev */}
@@ -581,7 +578,7 @@ export function SearchOverlay({
onKeyDown={handleKeyDown}
leadingVisual={<SearchIcon />}
aria-labelledby={overlayHeadingId}
placeholder={t('search.input.placeholder')}
placeholder={t('search.input.placeholder_no_icon')}
trailingAction={
<Stack
justify="center"
@@ -697,6 +694,7 @@ function renderSearchGroups(
const askAIGroupHeading = (
<ActionList.GroupHeading
key="ai-heading"
as="h3"
tabIndex={-1}
aria-label={t('search.overlay.ai_suggestions_list_aria_label')}
@@ -732,12 +730,13 @@ function renderSearchGroups(
let isInAskAIStateButNoAnswer = isInAskAIState && askAIState.aiCouldNotAnswer
if (isInAskAIStateButNoAnswer) {
groups.push(<ActionList.Divider key="general-divider" />)
groups.push(<ActionList.Divider key="no-answer-divider" />)
}
if (searchLoading) {
groups.push(
<Box
key="loading"
role="status"
className={styles.loadingContainer}
sx={{
@@ -831,13 +830,13 @@ function renderSearchGroups(
(option) => !option.isViewAllResults && !option.isNoResultsFound,
).length)
) {
groups.push(<ActionList.Divider key="general-divider" />)
groups.push(<ActionList.Divider key="bottom-divider" />)
}
}
if (aiOptionsWithUserInput.length && !isInAskAIState) {
groups.push(
<ActionList.Group key="ai" data-testid="ai-autocomplete-suggestions">
<ActionList.Group key="ai-suggestions" data-testid="ai-autocomplete-suggestions">
<ActionList.GroupHeading
as="h3"
tabIndex={-1}