Skip to content

Commit 448ee41

Browse files
committed
fix(webapp): keep tooltip controls keyboard accessible
1 parent d6f6545 commit 448ee41

7 files changed

Lines changed: 73 additions & 50 deletions

File tree

apps/webapp/app/components/AskAI.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -544,6 +544,7 @@ function ChatInterface({ initialQuery }: { initialQuery?: string }) {
544544
{isGeneratingAnswer ? (
545545
<SimpleTooltip
546546
asChild
547+
tabbable
547548
button={
548549
<button
549550
type="button"

apps/webapp/app/components/code/TSQLResultsTable.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -858,6 +858,7 @@ function CopyableCell({
858858
<span className="flex items-center truncate">{children}</span>
859859
<SimpleTooltip
860860
asChild
861+
tabbable
861862
button={
862863
<button
863864
type="button"

apps/webapp/app/components/dashboard-agent/tooltip-accessible-name.test.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,16 @@ function attrOf(node: JsxNode, name: string) {
8989
return open.attributes.properties.find((p) => ts.isJsxAttribute(p) && p.name.getText() === name);
9090
}
9191

92+
function hasStaticTrueAttribute(node: JsxNode, name: string): boolean {
93+
const attribute = attrOf(node, name);
94+
if (!attribute || !ts.isJsxAttribute(attribute)) return false;
95+
if (!attribute.initializer) return true;
96+
return (
97+
ts.isJsxExpression(attribute.initializer) &&
98+
attribute.initializer.expression?.kind === ts.SyntaxKind.TrueKeyword
99+
);
100+
}
101+
92102
/** Text anywhere under the element, ignoring an expression that can render nothing. */
93103
function hasText(node: TsNode): boolean {
94104
if (!ts.isJsxElement(node)) return false;
@@ -178,7 +188,7 @@ function scanFile(file: string, relative: string): Violation[] {
178188
const initializer =
179189
buttonAttr && ts.isJsxAttribute(buttonAttr) ? buttonAttr.initializer : undefined;
180190
if (initializer && ts.isJsxExpression(initializer)) {
181-
const asChild = !!attrOf(node, "asChild");
191+
const asChild = hasStaticTrueAttribute(node, "asChild");
182192
for (const trigger of resolve(initializer.expression).flatMap(triggersIn)) {
183193
const named =
184194
!!attrOf(trigger, "aria-label") ||

apps/webapp/app/components/primitives/CopyButton.tsx

Lines changed: 55 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -44,61 +44,68 @@ export function CopyButton({
4444

4545
const { icon: iconSize, button: buttonSize } = sizes[size];
4646

47-
const button =
48-
variant === "icon" ? (
49-
<button
50-
type="button"
51-
aria-label={copied ? "Copied" : "Copy"}
52-
onClick={copy}
53-
className={cn(
54-
buttonSize,
55-
"flex items-center justify-center rounded border border-border-bright bg-background-hover",
56-
copied
57-
? "text-green-500"
58-
: "text-text-dimmed hover:border-border-bright hover:bg-background-raised hover:text-text-bright",
59-
buttonClassName
60-
)}
61-
>
62-
{copied ? (
63-
<ClipboardCheckIcon className={iconSize} />
64-
) : (
65-
<ClipboardIcon className={iconSize} />
66-
)}
67-
</button>
68-
) : (
69-
<Button
70-
variant={`${buttonVariant}/${size === "extra-small" ? "small" : size}`}
71-
onClick={copy}
72-
className={cn("shrink-0", buttonClassName)}
73-
LeadingIcon={
74-
copied ? (
75-
<ClipboardCheckIcon
76-
className={cn(
77-
iconSize,
78-
buttonVariant === "primary" ? "text-background-dimmed" : "text-green-500"
79-
)}
80-
/>
81-
) : (
82-
<ClipboardIcon
83-
className={cn(
84-
iconSize,
85-
buttonVariant === "primary" ? "text-background-dimmed" : "text-text-dimmed"
86-
)}
87-
/>
88-
)
89-
}
90-
>
91-
{children}
92-
</Button>
47+
if (variant === "button") {
48+
return (
49+
<span className={className}>
50+
<Button
51+
variant={`${buttonVariant}/${size === "extra-small" ? "small" : size}`}
52+
onClick={copy}
53+
className={cn("shrink-0", buttonClassName)}
54+
tooltip={showTooltip ? (copied ? "Copied!" : "Copy") : undefined}
55+
LeadingIcon={
56+
copied ? (
57+
<ClipboardCheckIcon
58+
className={cn(
59+
iconSize,
60+
buttonVariant === "primary" ? "text-background-dimmed" : "text-green-500"
61+
)}
62+
/>
63+
) : (
64+
<ClipboardIcon
65+
className={cn(
66+
iconSize,
67+
buttonVariant === "primary" ? "text-background-dimmed" : "text-text-dimmed"
68+
)}
69+
/>
70+
)
71+
}
72+
>
73+
{children}
74+
</Button>
75+
</span>
9376
);
77+
}
9478

95-
if (!showTooltip) return <span className={className}>{button}</span>;
79+
const iconButton = (
80+
<button
81+
type="button"
82+
aria-label={copied ? "Copied" : "Copy"}
83+
onClick={copy}
84+
className={cn(
85+
buttonSize,
86+
"flex items-center justify-center rounded border border-border-bright bg-background-hover",
87+
copied
88+
? "text-green-500"
89+
: "text-text-dimmed hover:border-border-bright hover:bg-background-raised hover:text-text-bright",
90+
buttonClassName
91+
)}
92+
>
93+
{copied ? (
94+
<ClipboardCheckIcon className={iconSize} />
95+
) : (
96+
<ClipboardIcon className={iconSize} />
97+
)}
98+
</button>
99+
);
100+
101+
if (!showTooltip) return <span className={className}>{iconButton}</span>;
96102

97103
return (
98104
<span className={className}>
99105
<SimpleTooltip
100106
asChild
101-
button={button}
107+
tabbable
108+
button={iconButton}
102109
content={copied ? "Copied!" : "Copy"}
103110
className="font-sans"
104111
disableHoverableContent

apps/webapp/app/components/primitives/CopyableText.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,11 +87,12 @@ export function CopyableText({
8787
iconButton
8888
) : (
8989
<SimpleTooltip
90+
asChild
91+
tabbable
9092
button={iconButton}
9193
content={copied ? "Copied!" : "Copy"}
9294
className="font-sans"
9395
disableHoverableContent
94-
asChild={asChild}
9596
/>
9697
)}
9798
</span>

apps/webapp/app/components/primitives/Table.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -488,6 +488,7 @@ export const CopyableTableCell = forwardRef<HTMLTableCellElement, CopyableTableC
488488
{children}
489489
<SimpleTooltip
490490
asChild
491+
tabbable
491492
button={
492493
<button
493494
type="button"

apps/webapp/app/components/runs/v3/RunTag.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ function CopyButton({ textToCopy, isHovered }: { textToCopy: string; isHovered:
114114
return (
115115
<SimpleTooltip
116116
asChild
117+
tabbable
117118
button={
118119
<button
119120
type="button"
@@ -162,6 +163,7 @@ function DeleteButton({
162163
return (
163164
<SimpleTooltip
164165
asChild
166+
tabbable
165167
button={
166168
<button
167169
type="button"

0 commit comments

Comments
 (0)