Skip to content
Merged
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
10 changes: 3 additions & 7 deletions apps/web/src/components/Panels/PreviewWorkspace/PreviewTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ export function PreviewTab({
zIndex: isDragging ? 1 : undefined,
}}
className={cn(
'group relative flex h-9 w-fit max-w-48 min-w-20 flex-[0_1_auto] cursor-pointer items-center gap-1.5 px-2.5 text-sm',
'group relative flex h-9 w-fit max-w-48 min-w-20 flex-none cursor-pointer items-center gap-1.5 px-2.5 text-sm',
'border-edge-default border-r',
'focus-visible:outline-info focus-visible:outline-1 focus-visible:-outline-offset-2',
isActive
Expand All @@ -186,7 +186,7 @@ export function PreviewTab({
<Tooltip
content={tabDescription}
placement="bottom"
wrapperClassName="inline-flex min-w-0"
wrapperClassName="inline-flex min-w-0 flex-1"
>
<span
data-testid="preview-tab-title"
Expand All @@ -197,11 +197,7 @@ export function PreviewTab({
</Tooltip>
<div
data-testid="preview-tab-actions"
className={cn(
'pointer-events-none absolute right-1 z-10 flex items-center gap-0 opacity-0 transition-opacity',
'group-hover:pointer-events-auto group-hover:opacity-100 focus-within:pointer-events-auto focus-within:opacity-100',
isActive ? 'bg-surface' : 'bg-hover',
)}
className="ml-auto flex shrink-0 items-center gap-0"
>
{tab.transient && (
<Button
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -248,15 +248,28 @@ describe('tab strip', () => {
expect(tabs()[0].classList.contains('h-9')).toBe(true);
});

it('sizes tabs to content and shrinks them before scrolling', () => {
it('sizes tabs to content without shrinking their controls', () => {
openNode('a');
openNode('b');
render([canvasNode('a', 'Alpha'), canvasNode('b', 'Beta')]);

const [inactiveTab, activeTab] = tabs();
expect(inactiveTab.classList.contains('min-w-20')).toBe(true);
expect(inactiveTab.classList.contains('w-fit')).toBe(true);
expect(inactiveTab.classList.contains('flex-[0_1_auto]')).toBe(true);
expect(inactiveTab.classList.contains('flex-none')).toBe(true);
expect(inactiveTab.classList.contains('flex-[0_1_auto]')).toBe(false);
for (const tab of [inactiveTab, activeTab]) {
const actionRail = tab.querySelector<HTMLElement>(
'[data-testid="preview-tab-actions"]',
);
expect(actionRail?.classList.contains('shrink-0')).toBe(true);
expect(actionRail?.classList.contains('absolute')).toBe(false);
expect(actionRail?.classList.contains('opacity-0')).toBe(false);
expect(actionRail?.classList.contains('pointer-events-none')).toBe(false);
expect(
actionRail?.querySelector('[aria-label^="Close "]'),
).not.toBeNull();
}
expect(activeTab.classList.contains('border-r')).toBe(true);
expect(activeTab.classList.contains('last:border-r-0')).toBe(false);
expect(activeTab.classList.contains('bg-surface')).toBe(true);
Expand Down Expand Up @@ -616,13 +629,12 @@ describe('activation', () => {
expect(title?.classList.contains('transition-colors')).toBe(true);
expect(icon?.classList.contains('group-hover:text-fg-subtle')).toBe(true);
expect(icon?.classList.contains('transition-colors')).toBe(true);
expect(actionRail?.classList.contains('absolute')).toBe(true);
expect(actionRail?.classList.contains('opacity-0')).toBe(true);
expect(actionRail?.classList.contains('absolute')).toBe(false);
expect(actionRail?.classList.contains('shrink-0')).toBe(true);
expect(actionRail?.classList.contains('opacity-0')).toBe(false);
expect(actionRail?.classList.contains('pointer-events-none')).toBe(false);
expect(actionRail?.classList.contains('group-hover:opacity-100')).toBe(
true,
);
expect(actionRail?.classList.contains('focus-within:opacity-100')).toBe(
true,
false,
);
expect(actionRail?.contains(keepButton ?? null)).toBe(true);
expect(actionRail?.contains(closeButton ?? null)).toBe(true);
Expand Down
2 changes: 1 addition & 1 deletion docs/architecture/preview-workspace.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ The separator exposes a symmetric pointer target around its visible rule, tracks

Each group uses the WAI-ARIA tabs pattern with a tablist, selected tab, labelled tabpanel, and roving keyboard focus. Only the focused group responds to group-level shortcuts; editable controls, search, menus, and media viewers keep ownership of their own keys.

Tab titles are visually truncated while retaining full accessible labels and tooltips. Transient tabs are visually distinct and expose their temporary status and promotion gesture accessibly.
Tab titles are visually truncated while retaining full accessible labels and tooltips. Tabs do not shrink their action controls when the strip is crowded; the strip scrolls horizontally instead. Close remains visible on every tab, and the one-way Pin action remains visible on transient tabs, so pointer, keyboard, and touch users do not depend on hover to operate a tab. Transient tabs are visually distinct and expose their temporary status and promotion gesture accessibly.

## 9. Integration rules

Expand Down
Loading