fix(ui): remove member row tooltip ref loop

This commit is contained in:
777genius 2026-05-17 00:45:09 +03:00
parent 3c483745d9
commit a2e82e498a
2 changed files with 60 additions and 41 deletions

View file

@ -96,15 +96,6 @@ vi.mock('@renderer/components/ui/MentionableTextarea', () => ({
MentionableTextarea: () => React.createElement('textarea'), MentionableTextarea: () => React.createElement('textarea'),
})); }));
vi.mock('@renderer/components/ui/tooltip', () => ({
Tooltip: ({ children }: { children: React.ReactNode }) =>
React.createElement(React.Fragment, null, children),
TooltipTrigger: ({ children }: { children: React.ReactNode }) =>
React.createElement(React.Fragment, null, children),
TooltipContent: ({ children }: { children: React.ReactNode }) =>
React.createElement('div', null, children),
}));
vi.mock('@renderer/hooks/useDraftPersistence', () => ({ vi.mock('@renderer/hooks/useDraftPersistence', () => ({
useDraftPersistence: ({ initialValue }: { initialValue?: string }) => ({ useDraftPersistence: ({ initialValue }: { initialValue?: string }) => ({
value: initialValue ?? '', value: initialValue ?? '',
@ -253,6 +244,29 @@ describe('MemberDraftRow', () => {
}); });
}); });
it('renders worktree isolation help without a Radix tooltip trigger', () => {
const { host, root } = renderMemberDraftRow({
showWorktreeIsolationControls: true,
worktreeIsolationDisabledReason: 'Worktree isolation is disabled for this project.',
});
const worktreeControl = host.querySelector<HTMLInputElement>(
'#member-member-1-worktree-isolation'
)!;
const descriptionId = 'member-member-1-worktree-isolation-description';
const wrapper = worktreeControl.closest('[title]');
expect(worktreeControl.getAttribute('aria-describedby')).toBe(descriptionId);
expect(wrapper?.getAttribute('title')).toBe('Worktree isolation is disabled for this project.');
expect(host.querySelector(`#${descriptionId}`)?.textContent).toBe(
'Worktree isolation is disabled for this project.'
);
act(() => {
root.unmount();
});
});
it('warns custom Anthropic Sonnet teammates about plan/runtime billing when 200K limit is off', () => { it('warns custom Anthropic Sonnet teammates about plan/runtime billing when 200K limit is off', () => {
const { host, root } = renderMemberDraftRow({ const { host, root } = renderMemberDraftRow({
member: { member: {

View file

@ -15,7 +15,6 @@ import { Checkbox } from '@renderer/components/ui/checkbox';
import { Input } from '@renderer/components/ui/input'; import { Input } from '@renderer/components/ui/input';
import { Label } from '@renderer/components/ui/label'; import { Label } from '@renderer/components/ui/label';
import { MentionableTextarea } from '@renderer/components/ui/MentionableTextarea'; import { MentionableTextarea } from '@renderer/components/ui/MentionableTextarea';
import { Tooltip, TooltipContent, TooltipTrigger } from '@renderer/components/ui/tooltip';
import { getTeamColorSet } from '@renderer/constants/teamColors'; import { getTeamColorSet } from '@renderer/constants/teamColors';
import { useDraftPersistence } from '@renderer/hooks/useDraftPersistence'; import { useDraftPersistence } from '@renderer/hooks/useDraftPersistence';
import { useFileListCacheWarmer } from '@renderer/hooks/useFileListCacheWarmer'; import { useFileListCacheWarmer } from '@renderer/hooks/useFileListCacheWarmer';
@ -234,6 +233,13 @@ export const MemberDraftRow = ({
: undefined; : undefined;
const worktreeIsolationDisabled = const worktreeIsolationDisabled =
isRemoved || Boolean(worktreeIsolationDisabledReason && member.isolation !== 'worktree'); isRemoved || Boolean(worktreeIsolationDisabledReason && member.isolation !== 'worktree');
const worktreeIsolationDescription =
worktreeIsolationDisabledReason && member.isolation !== 'worktree'
? worktreeIsolationDisabledReason
: 'Run this teammate in a separate git worktree. Apply/reject changes targets that worktree, not the lead workspace.';
const worktreeIsolationDescriptionId = showWorktreeIsolationControls
? `member-${member.id}-worktree-isolation-description`
: undefined;
const effectiveModelKey = effectiveModel?.trim() ?? ''; const effectiveModelKey = effectiveModel?.trim() ?? '';
const selectedModelIssueText = const selectedModelIssueText =
effectiveModelKey && modelIssueReasonByProvider?.[effectiveProviderId]?.[effectiveModelKey] effectiveModelKey && modelIssueReasonByProvider?.[effectiveProviderId]?.[effectiveModelKey]
@ -393,40 +399,39 @@ export const MemberDraftRow = ({
) : null} ) : null}
</div> </div>
{showWorktreeIsolationControls ? ( {showWorktreeIsolationControls ? (
<Tooltip> <div className="space-y-0.5">
<TooltipTrigger asChild> <div
<div className={cn(
'flex h-8 shrink-0 cursor-pointer items-center gap-1.5 rounded-md border border-[var(--color-border)] px-2 text-xs text-[var(--color-text-secondary)]',
worktreeIsolationDisabled && 'cursor-not-allowed opacity-50'
)}
title={worktreeIsolationDescription}
aria-describedby={worktreeIsolationDescriptionId}
>
<Checkbox
id={`member-${member.id}-worktree-isolation`}
checked={member.isolation === 'worktree'}
disabled={worktreeIsolationDisabled}
aria-describedby={worktreeIsolationDescriptionId}
onCheckedChange={(checked) =>
onWorktreeIsolationChange?.(member.id, checked === true)
}
/>
<Label
htmlFor={`member-${member.id}-worktree-isolation`}
className={cn( className={cn(
'flex h-8 shrink-0 cursor-pointer items-center gap-1.5 rounded-md border border-[var(--color-border)] px-2 text-xs text-[var(--color-text-secondary)]', 'flex cursor-pointer items-center gap-1.5 text-xs font-normal',
worktreeIsolationDisabled && 'cursor-not-allowed opacity-50' worktreeIsolationDisabled && 'cursor-not-allowed'
)} )}
> >
<Checkbox <GitBranch className="size-3.5 shrink-0" />
id={`member-${member.id}-worktree-isolation`} <span>Worktree</span>
checked={member.isolation === 'worktree'} </Label>
disabled={worktreeIsolationDisabled} </div>
onCheckedChange={(checked) => <span id={worktreeIsolationDescriptionId} className="sr-only">
onWorktreeIsolationChange?.(member.id, checked === true) {worktreeIsolationDescription}
} </span>
/> </div>
<Label
htmlFor={`member-${member.id}-worktree-isolation`}
className={cn(
'flex cursor-pointer items-center gap-1.5 text-xs font-normal',
worktreeIsolationDisabled && 'cursor-not-allowed'
)}
>
<GitBranch className="size-3.5 shrink-0" />
<span>Worktree</span>
</Label>
</div>
</TooltipTrigger>
<TooltipContent side="top" className="max-w-64 text-xs leading-relaxed">
{worktreeIsolationDisabledReason && member.isolation !== 'worktree'
? worktreeIsolationDisabledReason
: 'Run this teammate in a separate git worktree. Apply/reject changes targets that worktree, not the lead workspace.'}
</TooltipContent>
</Tooltip>
) : null} ) : null}
{hideActionButton ? null : isRemoved ? ( {hideActionButton ? null : isRemoved ? (
<Button <Button