fix(team): keep member launch labels consistent
This commit is contained in:
parent
0821b182e2
commit
e8ebe68576
6 changed files with 49 additions and 8 deletions
|
|
@ -126,6 +126,10 @@ export const MemberCard = ({
|
||||||
const spawnCardClass = launchPresentation.cardClass;
|
const spawnCardClass = launchPresentation.cardClass;
|
||||||
const launchVisualState = launchPresentation.launchVisualState;
|
const launchVisualState = launchPresentation.launchVisualState;
|
||||||
const launchStatusLabel = launchPresentation.launchStatusLabel;
|
const launchStatusLabel = launchPresentation.launchStatusLabel;
|
||||||
|
const displayPresenceLabel =
|
||||||
|
launchVisualState === 'runtime_pending' || launchVisualState === 'permission_pending'
|
||||||
|
? (launchStatusLabel ?? presenceLabel)
|
||||||
|
: presenceLabel;
|
||||||
const colors = getTeamColorSet(memberColor);
|
const colors = getTeamColorSet(memberColor);
|
||||||
const { isLight } = useTheme();
|
const { isLight } = useTheme();
|
||||||
const pending = taskCounts?.pending ?? 0;
|
const pending = taskCounts?.pending ?? 0;
|
||||||
|
|
@ -155,8 +159,7 @@ export const MemberCard = ({
|
||||||
(presenceLabel === 'starting' ||
|
(presenceLabel === 'starting' ||
|
||||||
presenceLabel === 'connecting' ||
|
presenceLabel === 'connecting' ||
|
||||||
launchVisualState === 'runtime_pending');
|
launchVisualState === 'runtime_pending');
|
||||||
const launchBadgeLabel =
|
const launchBadgeLabel = presenceLabel === 'starting' ? presenceLabel : displayPresenceLabel;
|
||||||
presenceLabel === 'starting' ? presenceLabel : (launchStatusLabel ?? presenceLabel);
|
|
||||||
const showRuntimeAdvisoryBadge =
|
const showRuntimeAdvisoryBadge =
|
||||||
!isRemoved &&
|
!isRemoved &&
|
||||||
Boolean(runtimeAdvisoryLabel) &&
|
Boolean(runtimeAdvisoryLabel) &&
|
||||||
|
|
@ -201,7 +204,7 @@ export const MemberCard = ({
|
||||||
</div>
|
</div>
|
||||||
<span
|
<span
|
||||||
className={`absolute -bottom-0.5 -right-0.5 size-2.5 rounded-full border-2 border-[var(--color-surface)] ${dotClass}`}
|
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>
|
||||||
<div className="min-w-0 flex-1">
|
<div className="min-w-0 flex-1">
|
||||||
|
|
@ -295,7 +298,7 @@ export const MemberCard = ({
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
className="shrink-0 bg-red-500/15 px-1.5 py-0.5 text-[10px] font-normal leading-none text-red-400"
|
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>
|
</Badge>
|
||||||
</span>
|
</span>
|
||||||
</TooltipTrigger>
|
</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)]'}`}
|
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}
|
title={isRemoved ? 'This member has been removed' : activityTitle}
|
||||||
>
|
>
|
||||||
{isRemoved ? 'removed' : presenceLabel}
|
{isRemoved ? 'removed' : displayPresenceLabel}
|
||||||
</Badge>
|
</Badge>
|
||||||
) : null}
|
) : null}
|
||||||
{showStartingSkeleton ? (
|
{showStartingSkeleton ? (
|
||||||
|
|
|
||||||
|
|
@ -105,7 +105,7 @@ export const MemberDetailHeader = ({
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
className={`absolute -bottom-0.5 -right-0.5 size-3 rounded-full border-2 border-[var(--color-surface)] ${dotClass}`}
|
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>
|
||||||
<div className="min-w-0 flex-1">
|
<div className="min-w-0 flex-1">
|
||||||
|
|
|
||||||
|
|
@ -157,7 +157,7 @@ export const MemberHoverCard = ({
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
className={`absolute -bottom-0.5 -right-0.5 size-3 rounded-full border-2 border-[var(--color-surface)] ${dotClass}`}
|
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>
|
||||||
<div className="min-w-0 flex-1">
|
<div className="min-w-0 flex-1">
|
||||||
|
|
|
||||||
|
|
@ -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 () => {
|
it('keeps the starting treatment and runtime summary visible while a runtime is still joining', async () => {
|
||||||
vi.stubGlobal('IS_REACT_ACT_ENVIRONMENT', true);
|
vi.stubGlobal('IS_REACT_ACT_ENVIRONMENT', true);
|
||||||
const host = document.createElement('div');
|
const host = document.createElement('div');
|
||||||
|
|
@ -258,7 +293,7 @@ describe('MemberCard starting-state visuals', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(host.textContent).toContain('awaiting permission');
|
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();
|
expect(host.querySelector('.member-waiting-shimmer')).not.toBeNull();
|
||||||
|
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
|
|
|
||||||
|
|
@ -123,6 +123,7 @@ describe('MemberDetailHeader spawn-aware presence', () => {
|
||||||
|
|
||||||
expect(host.textContent).toContain('connecting');
|
expect(host.textContent).toContain('connecting');
|
||||||
expect(host.textContent).not.toContain('online');
|
expect(host.textContent).not.toContain('online');
|
||||||
|
expect(host.querySelector('[aria-label="connecting"]')).not.toBeNull();
|
||||||
|
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
root.unmount();
|
root.unmount();
|
||||||
|
|
|
||||||
|
|
@ -190,6 +190,7 @@ describe('MemberHoverCard spawn-aware presence', () => {
|
||||||
|
|
||||||
expect(host.textContent).toContain('connecting');
|
expect(host.textContent).toContain('connecting');
|
||||||
expect(host.textContent).not.toContain('online');
|
expect(host.textContent).not.toContain('online');
|
||||||
|
expect(host.querySelector('[aria-label="connecting"]')).not.toBeNull();
|
||||||
|
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
root.unmount();
|
root.unmount();
|
||||||
|
|
@ -225,6 +226,7 @@ describe('MemberHoverCard spawn-aware presence', () => {
|
||||||
|
|
||||||
expect(host.textContent).toContain('connecting');
|
expect(host.textContent).toContain('connecting');
|
||||||
expect(host.textContent).not.toContain('online');
|
expect(host.textContent).not.toContain('online');
|
||||||
|
expect(host.querySelector('[aria-label="connecting"]')).not.toBeNull();
|
||||||
|
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
root.unmount();
|
root.unmount();
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue