Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ afterEach(() => {

describe("secondary panel tab-strip edge fades", () => {
it("uses the themed edge fade", () => {
expect(SECONDARY_PANEL_TAB_STRIP_FADE_TONE).toBe("sidebar");
expect(SECONDARY_PANEL_TAB_STRIP_FADE_TONE).toBe("surface-raised");
});

it("observes the intrinsic tab row so async title changes refresh overflow", () => {
Expand Down Expand Up @@ -87,6 +87,8 @@ describe("secondary panel tab-strip edge fades", () => {
);
expect(leftButton?.classList.contains("w-0")).toBe(true);
expect(rightButton?.classList.contains("w-0")).toBe(true);
expect(strip?.hasAttribute("data-overflowing")).toBe(false);
expect(strip?.classList.contains("border-border-hairline")).toBe(false);

const rightFade = container.querySelector("[data-overflow-fade='right']");
expect(rightFade?.classList.contains("opacity-0")).toBe(true);
Expand Down Expand Up @@ -122,11 +124,12 @@ describe("secondary panel tab-strip edge fades", () => {
expect(leftButton?.tabIndex).toBe(-1);
expect(rightButton?.classList.contains("opacity-100")).toBe(true);
expect(rightButton?.tabIndex).toBe(0);
expect(rightButton?.classList.contains("bg-sidebar")).toBe(true);
expect(
rightButton?.classList.contains("hover:bg-surface-raised-solid"),
).toBe(true);
expect(rightButton?.classList.contains("hover:bg-state-hover")).toBe(false);
expect(strip?.hasAttribute("data-overflowing")).toBe(true);
expect(strip?.classList.contains("rounded-md")).toBe(true);
expect(strip?.classList.contains("border-border-hairline")).toBe(true);
expect(strip?.classList.contains("bg-surface-raised-solid")).toBe(true);
expect(rightButton?.classList.contains("bg-transparent")).toBe(true);
expect(rightButton?.classList.contains("hover:bg-state-hover")).toBe(true);

const scrollBy = vi.fn();
Object.defineProperty(viewport!, "scrollBy", {
Expand Down Expand Up @@ -154,6 +157,8 @@ describe("secondary panel tab-strip edge fades", () => {
});
expect(leftButton?.classList.contains("w-0")).toBe(true);
expect(rightButton?.classList.contains("w-0")).toBe(true);
expect(strip?.hasAttribute("data-overflowing")).toBe(false);
expect(strip?.classList.contains("border-border-hairline")).toBe(false);
expect(document.activeElement).toBe(
container.querySelector('button[aria-pressed="true"]'),
);
Expand Down
46 changes: 36 additions & 10 deletions apps/app/src/components/secondary-panel/SecondaryPanelTabStrip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import {
OverflowFade,
type OverflowFadeTone,
} from "@/components/ui/overflow-fade";
import { TabPill } from "@/components/ui/tab-pill";
import { TabPill, type TabPillActiveTreatment } from "@/components/ui/tab-pill";
import { useDragClickSuppression } from "@/components/ui/use-drag-click-suppression";
import { cn } from "@bb/shared-ui/lib/utils";
import {
Expand All @@ -57,7 +57,8 @@ const TAB_STRIP_SCROLL_BUTTON_CLASS =
// Slack so sub-pixel scroll offsets don't leave an overflow cue at a hard edge.
const EDGE_EPSILON_PX = 1;

export const SECONDARY_PANEL_TAB_STRIP_FADE_TONE: OverflowFadeTone = "sidebar";
export const SECONDARY_PANEL_TAB_STRIP_FADE_TONE: OverflowFadeTone =
"surface-raised";

/**
* Stand-in for dnd-kit's TouchSensor while the panel is closed or has nothing
Expand Down Expand Up @@ -86,6 +87,7 @@ const INITIAL_OVERFLOW_STATE: TabStripOverflowState = {
};

export interface SecondaryPanelTabStripProps {
activeClassName?: string;
fileTabs: SecondaryPanelFileTab[];
onBeginTabDrag?: (
tabId: string,
Expand All @@ -100,11 +102,12 @@ export interface SecondaryPanelTabStripProps {
* every page.
*/
isPanelOpen: boolean;
activeTreatment?: "fill" | "underline";
activeTreatment?: TabPillActiveTreatment;
}

interface SortableFileTabProps {
activeTreatment: "fill" | "underline";
activeClassName?: string;
activeTreatment: TabPillActiveTreatment;
activeTabRef: RefObject<HTMLDivElement | null>;
dragDisabled: boolean;
noDragClass: string | null;
Expand All @@ -125,6 +128,7 @@ interface SortableFileTabProps {
* (covering pointer, keyboard, and programmatic selection).
*/
export function SecondaryPanelTabStrip({
activeClassName,
fileTabs,
onBeginTabDrag,
onReorderTab,
Expand Down Expand Up @@ -436,6 +440,7 @@ export function SecondaryPanelTabStrip({
{fileTabs.map((tab) => (
<SortableFileTab
key={tab.id}
activeClassName={activeClassName}
activeTreatment={activeTreatment}
activeTabRef={activeTabRef}
dragDisabled={dragDisabled}
Expand All @@ -452,7 +457,11 @@ export function SecondaryPanelTabStrip({
{createPortal(
<DragOverlay className="cursor-grabbing">
{draggingTab === null ? null : (
<FileTab tab={draggingTab} activeTreatment={activeTreatment} />
<FileTab
tab={draggingTab}
activeClassName={activeClassName}
activeTreatment={activeTreatment}
/>
)}
</DragOverlay>,
document.body,
Expand All @@ -471,6 +480,7 @@ export function SecondaryPanelTabStrip({
onBeginTabDrag,
draggingTab,
activeTreatment,
activeClassName,
],
);

Expand All @@ -482,7 +492,12 @@ export function SecondaryPanelTabStrip({
<div
ref={stripRef}
data-testid="secondary-panel-tab-strip"
className="group relative flex min-w-0 items-center"
data-overflowing={overflow.hasOverflow ? "" : undefined}
className={cn(
"group relative flex min-w-0 items-center",
overflow.hasOverflow &&
"rounded-md border border-border-hairline bg-surface-raised-solid",
)}
>
<TabStripScrollButton
buttonRef={leftScrollButtonRef}
Expand Down Expand Up @@ -543,6 +558,7 @@ export function SecondaryPanelTabStrip({
}

function SortableFileTab({
activeClassName,
activeTreatment,
activeTabRef,
dragDisabled,
Expand Down Expand Up @@ -592,7 +608,11 @@ function SortableFileTab({
}}
{...sortableListeners}
>
<FileTab tab={tab} activeTreatment={activeTreatment} />
<FileTab
tab={tab}
activeClassName={activeClassName}
activeTreatment={activeTreatment}
/>
</div>
);
}
Expand Down Expand Up @@ -626,9 +646,12 @@ function TabStripScrollButton({
aria-label={label}
onClick={onClick}
className={cn(
"z-20 shrink-0 bg-sidebar text-muted-foreground shadow-none hover:bg-surface-raised-solid hover:text-foreground focus-visible:bg-sidebar",
"z-20 shrink-0 text-muted-foreground shadow-none",
hasOverflow
? TAB_STRIP_SCROLL_BUTTON_CLASS
? [
TAB_STRIP_SCROLL_BUTTON_CLASS,
"bg-transparent hover:bg-state-hover hover:text-foreground focus-visible:bg-state-hover",
]
: "h-7 w-0 overflow-hidden p-0 max-md:pointer-coarse:h-9",
"transition-opacity",
canScroll
Expand All @@ -644,10 +667,12 @@ function TabStripScrollButton({

function FileTab({
tab,
activeClassName,
activeTreatment,
}: {
tab: SecondaryPanelFileTab;
activeTreatment: "fill" | "underline";
activeClassName?: string;
activeTreatment: TabPillActiveTreatment;
}) {
const title =
tab.statusLabel === null
Expand All @@ -660,6 +685,7 @@ function FileTab({
secondaryLabel={tab.statusLabel === null ? null : `(${tab.statusLabel})`}
title={title}
isActive={tab.isActive}
activeClassName={activeClassName}
activeTreatment={activeTreatment}
onSelect={tab.onSelect}
labelMaxWidthClass="max-w-[160px]"
Expand Down
Loading
Loading