Skip to content

Commit d6a5a2f

Browse files
maria-rcksclaude
andcommitted
fix(web): keep the project machine badge stable and describe it accurately
Sort remote members by label so the icon and tooltip do not follow registration order, give the badge span an img role, say "Also on" when the project lives here too, and drop the unused className prop. Co-Authored-By: Claude Fable 5.1 <[email protected]>
1 parent 1147bdf commit d6a5a2f

1 file changed

Lines changed: 15 additions & 13 deletions

File tree

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,44 @@
11
import type { EnvironmentId, EnvironmentMachineKind } from "@t3tools/contracts";
22

3-
import { cn } from "~/lib/utils";
43
import type { SidebarProjectSnapshot } from "~/sidebarProjectGrouping";
54
import { EnvironmentMachineIcon } from "./EnvironmentMachineIcon";
65
import { Tooltip, TooltipPopup, TooltipTrigger } from "./ui/tooltip";
76

87
/**
98
* Machine icon for a project picker row whose group has a member on another
109
* environment, with the environment names in a tooltip. Projects that only
11-
* live on this device render nothing, the same rule thread rows use. Callers
10+
* live on this device render nothing, the rule thread rows use for their
11+
* machine icon. Callers
1212
* render it only while the catalog spans environments (see
1313
* projectGroupsSpanEnvironments), so single-machine users see no change.
1414
*/
1515
export function ProjectEnvironmentBadge(props: {
1616
readonly group: Pick<SidebarProjectSnapshot, "memberProjects">;
1717
readonly primaryEnvironmentId: EnvironmentId | null;
1818
readonly machineByEnvironmentId: ReadonlyMap<EnvironmentId, EnvironmentMachineKind>;
19-
readonly className?: string;
2019
}) {
21-
const remoteMembers = props.group.memberProjects.filter(
22-
(member) => member.environmentId !== props.primaryEnvironmentId,
23-
);
20+
// Member order follows registration order and can differ between sessions,
21+
// so sort by label to keep the icon and tooltip stable.
22+
const remoteMembers = props.group.memberProjects
23+
.filter((member) => member.environmentId !== props.primaryEnvironmentId)
24+
.map((member) => ({ ...member, environmentLabel: member.environmentLabel ?? "Remote" }))
25+
.sort((a, b) => a.environmentLabel.localeCompare(b.environmentLabel));
2426
const first = remoteMembers[0];
2527
if (!first) return null;
2628
const labels = remoteMembers
27-
.map((member) => member.environmentLabel ?? "Remote")
29+
.map((member) => member.environmentLabel)
2830
.filter((label, index, all) => all.indexOf(label) === index)
2931
.join(", ");
32+
const alsoHere = remoteMembers.length < props.group.memberProjects.length;
33+
const description = `${alsoHere ? "Also on" : "On"} ${labels}`;
3034
return (
3135
<Tooltip>
3236
<TooltipTrigger
3337
render={
3438
<span
35-
aria-label={`Remote environment: ${labels}`}
36-
className={cn(
37-
"ml-auto inline-flex shrink-0 items-center text-muted-foreground",
38-
props.className,
39-
)}
39+
role="img"
40+
aria-label={description}
41+
className="ml-auto inline-flex shrink-0 items-center text-muted-foreground"
4042
/>
4143
}
4244
>
@@ -46,7 +48,7 @@ export function ProjectEnvironmentBadge(props: {
4648
className="size-3.5"
4749
/>
4850
</TooltipTrigger>
49-
<TooltipPopup side="top">{labels}</TooltipPopup>
51+
<TooltipPopup side="top">{description}</TooltipPopup>
5052
</Tooltip>
5153
);
5254
}

0 commit comments

Comments
 (0)