fix(desktop): avoid overlapping channel selection paint

The sidebar updates active ownership immediately during deferred navigation,
but its shared background transition visually retained the old active row while
fading in the new one. Exclude background and text colors from channel-row
transitions so selection changes atomically, and pin both singular ownership
and transition behavior at the pre-route paint boundary.

Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>
Signed-off-by: Wes <wesbillman@users.noreply.github.com>
This commit is contained in:
Wes
2026-08-16 10:11:38 -06:00
co-authored by Carl
parent 4b399d8a91
commit 73101d2d7f
2 changed files with 31 additions and 4 deletions
@@ -302,7 +302,10 @@ export function ChannelMenuButton({
const button = (
<SidebarMenuButton
className={cn(
"data-[active=true]:font-normal",
// Selection ownership changes immediately during deferred navigation.
// Do not cross-fade the old and new active backgrounds: that makes two
// channels appear selected even though data-active is already singular.
"data-[active=true]:font-normal transition-[width,height,padding,box-shadow]",
isActive
? "group-hover/menu-item:bg-sidebar-active group-hover/menu-item:text-sidebar-active-foreground"
: "group-hover/menu-item:bg-sidebar-accent group-hover/menu-item:text-sidebar-foreground",
+27 -3
View File
@@ -2789,9 +2789,14 @@ test("sidebar selection paints before cached channel work starts", async ({
await expect(page.getByTestId("chat-title")).toHaveText("general");
await page.evaluate(() => {
const observed = { selectedBeforeRoute: false };
const observed = {
selectedBeforeRoute: false,
singularSelection: false,
activeBackgroundTransitions: true,
};
Object.assign(window, { __BUZZ_SIDEBAR_SELECTION_OBSERVED__: observed });
const observer = new MutationObserver(() => {
const general = document.querySelector('[data-testid="channel-general"]');
const random = document.querySelector('[data-testid="channel-random"]');
if (random?.getAttribute("data-active") !== "true") return;
observer.disconnect();
@@ -2799,6 +2804,19 @@ test("sidebar selection paints before cached channel work starts", async ({
requestAnimationFrame(() => {
const title = document.querySelector('[data-testid="chat-title"]');
observed.selectedBeforeRoute = title?.textContent === "general";
observed.singularSelection =
general?.getAttribute("data-active") === "false" &&
document.querySelectorAll(
'[data-testid^="channel-"][data-active="true"]',
).length === 1;
observed.activeBackgroundTransitions = [general, random].some(
(element) =>
element instanceof HTMLElement &&
getComputedStyle(element)
.transitionProperty.split(",")
.map((property) => property.trim())
.includes("background-color"),
);
});
});
});
@@ -2819,12 +2837,18 @@ test("sidebar selection paints before cached channel work starts", async ({
window as Window & {
__BUZZ_SIDEBAR_SELECTION_OBSERVED__?: {
selectedBeforeRoute: boolean;
singularSelection: boolean;
activeBackgroundTransitions: boolean;
};
}
).__BUZZ_SIDEBAR_SELECTION_OBSERVED__?.selectedBeforeRoute ?? false,
).__BUZZ_SIDEBAR_SELECTION_OBSERVED__ ?? null,
),
)
.toBe(true);
.toEqual({
selectedBeforeRoute: true,
singularSelection: true,
activeBackgroundTransitions: false,
});
await expect(page.getByTestId("chat-title")).toHaveText("random");
});