Skip to content

Commit 9155e26

Browse files
authored
Fix AI search copy button aria-label for better accessibility (#57258)
1 parent 1b81cfe commit 9155e26

File tree

3 files changed

+34
-3
lines changed

3 files changed

+34
-3
lines changed

data/ui.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ search:
6363
invalid_query: Sorry, I'm unable to answer that question. Please try asking a different question.
6464
response:
6565
copy_code: Copy code to clipboard
66+
copy_code_lang: Copy {language} code to clipboard, block {block}
6667
copied_code: Copied!
6768
failure:
6869
general_title: There was an error loading search results.

src/fixtures/fixtures/data/ui.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ search:
6363
invalid_query: Sorry, I'm unable to answer that question. Please try asking a different question.
6464
response:
6565
copy_code: Copy code to clipboard
66+
copy_code_lang: Copy {language} code to clipboard, block {block}
6667
copied_code: Copied!
6768
failure:
6869
general_title: There was an error loading search results.

src/frame/components/ui/MarkdownContent/UnrenderedMarkdownContent.tsx

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,21 @@ import useCopyClipboard from '@/rest/components/useClipboard'
1212
import { EventType } from '@/events/types'
1313
import { sendEvent } from '@/events/components/events'
1414

15+
// Create a unique identifier by sampling characters distributed across content
16+
function getBlockId(text: string, targetLength: number = 8) {
17+
const alphanumeric = text.replace(/[^a-zA-Z0-9]+/g, '').toLowerCase()
18+
if (!alphanumeric || alphanumeric.length <= targetLength) {
19+
return Math.random().toString(36).substring(2, 10)
20+
}
21+
const step = alphanumeric.length / targetLength
22+
let result = ''
23+
for (let i = 0; i < targetLength; i++) {
24+
const index = Math.floor(i * step)
25+
result += alphanumeric[index]
26+
}
27+
return result
28+
}
29+
1530
export type MarkdownContentPropsT = {
1631
children: string
1732
className?: string
@@ -35,6 +50,7 @@ export const UnrenderedMarkdownContent = ({
3550
...restProps
3651
}: MarkdownContentPropsT) => {
3752
const { t } = useTranslation('search')
53+
3854
// Overrides for ReactMarkdown components
3955
const components = {} as Components
4056
if (codeBlocksCopyable) {
@@ -50,19 +66,32 @@ export const UnrenderedMarkdownContent = ({
5066
text = text.replace(/\n$/, '')
5167
}
5268

69+
// Extract language from className for better accessibility
70+
const language = props.className?.startsWith('language-')
71+
? props.className.replace('language-', '')
72+
: ''
73+
5374
const [isCopied, copyToClipboard] = useCopyClipboard(text, {
5475
successDuration: 2000,
5576
})
5677

78+
// Create more descriptive aria-label
79+
const getAriaLabel = () => {
80+
if (isCopied) {
81+
return t('search.ai.response.copied_code')
82+
}
83+
return t('search.ai.response.copy_code_lang')
84+
.replace('{language}', language ? `${language} ` : '')
85+
.replace('{block}', getBlockId(text))
86+
}
87+
5788
return (
5889
<div style={{ position: 'relative' }}>
5990
<IconButton
6091
size="small"
6192
icon={isCopied ? CheckIcon : CopyIcon}
6293
className="btn-octicon"
63-
aria-label={
64-
isCopied ? t('search.ai.response.copied_code') : t('search.ai.response.copy_code')
65-
}
94+
aria-label={getAriaLabel()}
6695
onClick={async () => {
6796
await copyToClipboard()
6897
announce(t('search.ai.response.copied_code'))

0 commit comments

Comments
 (0)