fix(team): keep member launch labels consistent

This commit is contained in:
777genius 2026-04-23 02:22:20 +03:00
parent 0821b182e2
commit e8ebe68576
6 changed files with 49 additions and 8 deletions

View file

@ -126,6 +126,10 @@ export const MemberCard = ({
const spawnCardClass = launchPresentation.cardClass;
const launchVisualState = launchPresentation.launchVisualState;
const launchStatusLabel = launchPresentation.launchStatusLabel;
const displayPresenceLabel =
launchVisualState === 'runtime_pending' || launchVisualState === 'permission_pending'
? (launchStatusLabel ?? presenceLabel)
: presenceLabel;
const colors = getTeamColorSet(memberColor);
const { isLight } = useTheme();
const pending = taskCounts?.pending ?? 0;
@ -155,8 +159,7 @@ export const MemberCard = ({
(presenceLabel === 'starting' ||
presenceLabel === 'connecting' ||
launchVisualState === 'runtime_pending');
const launchBadgeLabel =
presenceLabel === 'starting' ? presenceLabel : (launchStatusLabel ?? presenceLabel);
const launchBadgeLabel = presenceLabel === 'starting' ? presenceLabel : displayPresenceLabel;
const showRuntimeAdvisoryBadge =
!isRemoved &&
Boolean(runtimeAdvisoryLabel) &&
@ -201,7 +204,7 @@ export const MemberCard = ({
</div>
<span
className={`absolute -bottom-0.5 -right-0.5 size-2.5 rounded-full border-2 border-[var(--color-surface)] ${dotClass}`}
aria-label={presenceLabel}
aria-label={displayPresenceLabel}
/>
</div>
<div className="min-w-0 flex-1">
@ -295,7 +298,7 @@ export const MemberCard = ({
variant="secondary"
className="shrink-0 bg-red-500/15 px-1.5 py-0.5 text-[10px] font-normal leading-none text-red-400"
>
{presenceLabel}
{displayPresenceLabel}
</Badge>
</span>
</TooltipTrigger>
@ -325,7 +328,7 @@ export const MemberCard = ({
className={`shrink-0 px-1.5 py-0.5 text-[10px] font-normal leading-none ${isRemoved ? 'bg-zinc-600 text-zinc-300' : 'text-[var(--color-text-muted)]'}`}
title={isRemoved ? 'This member has been removed' : activityTitle}
>
{isRemoved ? 'removed' : presenceLabel}
{isRemoved ? 'removed' : displayPresenceLabel}
</Badge>
) : null}
{showStartingSkeleton ? (

View file

@ -105,7 +105,7 @@ export const MemberDetailHeader = ({
/>
<span
className={`absolute -bottom-0.5 -right-0.5 size-3 rounded-full border-2 border-[var(--color-surface)] ${dotClass}`}
aria-label={presenceLabel}
aria-label={badgeLabel}
/>
</div>
<div className="min-w-0 flex-1">

View file

@ -157,7 +157,7 @@ export const MemberHoverCard = ({
/>
<span
className={`absolute -bottom-0.5 -right-0.5 size-3 rounded-full border-2 border-[var(--color-surface)] ${dotClass}`}
aria-label={presenceLabel}
aria-label={badgeLabel}
/>
</div>
<div className="min-w-0 flex-1">

View file

@ -201,6 +201,41 @@ describe('MemberCard starting-state visuals', () => {
});
});
it('keeps runtime-pending accessibility copy honest even when launch badge is hidden by an active task', async () => {
vi.stubGlobal('IS_REACT_ACT_ENVIRONMENT', true);
const host = document.createElement('div');
document.body.appendChild(host);
const root = createRoot(host);
await act(async () => {
root.render(
React.createElement(MemberCard, {
member: {
...member,
currentTaskId: currentTask.id,
},
memberColor: 'blue',
currentTask,
isTeamAlive: true,
isTeamProvisioning: false,
spawnStatus: 'online',
spawnLaunchState: 'runtime_pending_bootstrap',
spawnRuntimeAlive: true,
spawnLivenessSource: 'process',
})
);
await Promise.resolve();
});
expect(host.textContent).not.toContain('online');
expect(host.querySelector('[aria-label="connecting"]')).not.toBeNull();
await act(async () => {
root.unmount();
await Promise.resolve();
});
});
it('keeps the starting treatment and runtime summary visible while a runtime is still joining', async () => {
vi.stubGlobal('IS_REACT_ACT_ENVIRONMENT', true);
const host = document.createElement('div');
@ -258,7 +293,7 @@ describe('MemberCard starting-state visuals', () => {
});
expect(host.textContent).toContain('awaiting permission');
expect(host.querySelector('[aria-label="connecting"]')).not.toBeNull();
expect(host.querySelector('[aria-label="awaiting permission"]')).not.toBeNull();
expect(host.querySelector('.member-waiting-shimmer')).not.toBeNull();
await act(async () => {

View file

@ -123,6 +123,7 @@ describe('MemberDetailHeader spawn-aware presence', () => {
expect(host.textContent).toContain('connecting');
expect(host.textContent).not.toContain('online');
expect(host.querySelector('[aria-label="connecting"]')).not.toBeNull();
await act(async () => {
root.unmount();

View file

@ -190,6 +190,7 @@ describe('MemberHoverCard spawn-aware presence', () => {
expect(host.textContent).toContain('connecting');
expect(host.textContent).not.toContain('online');
expect(host.querySelector('[aria-label="connecting"]')).not.toBeNull();
await act(async () => {
root.unmount();
@ -225,6 +226,7 @@ describe('MemberHoverCard spawn-aware presence', () => {
expect(host.textContent).toContain('connecting');
expect(host.textContent).not.toContain('online');
expect(host.querySelector('[aria-label="connecting"]')).not.toBeNull();
await act(async () => {
root.unmount();