Fix assistant drawer close affordance on mobile

This commit is contained in:
rcourtman
2026-05-20 17:21:06 +01:00
parent 93ba610892
commit bfff0490e6
2 changed files with 41 additions and 26 deletions
@@ -508,6 +508,20 @@ describe('AIChat', () => {
expect(screen.queryByTitle('Collapse Pulse Assistant')).not.toBeInTheDocument();
expect(screen.getByLabelText('Close Pulse Assistant')).toBeInTheDocument();
});
it('keeps the phone close button outside the scrollable assistant header actions', () => {
setViewportWidth(375);
renderChat();
const closeButton = screen.getByRole('button', { name: 'Close Pulse Assistant' });
const headerActions = screen.getByTestId('assistant-header-actions');
expect(closeButton).toHaveClass('order-2');
expect(headerActions).toHaveClass('order-3');
expect(headerActions).toHaveClass('w-full');
expect(headerActions).toHaveClass('overflow-x-auto');
expect(headerActions).not.toContainElement(closeButton);
});
});
// ── Close button ─────────────────────────────────────────────────────
@@ -9,6 +9,7 @@ import {
createEffect,
} from 'solid-js';
import { unwrap } from 'solid-js/store';
import XIcon from 'lucide-solid/icons/x';
import { AIAPI } from '@/api/ai';
import { AIChatAPI, type ChatSession, type ChatSessionHandoffSummary } from '@/api/aiChat';
import { notificationStore } from '@/stores/notifications';
@@ -1133,7 +1134,7 @@ export const AIChat: Component<AIChatProps> = (props) => {
type="button"
class="fixed inset-0 z-40 bg-slate-950/45 backdrop-blur-[1px]"
onClick={props.onClose}
aria-label="Close Pulse Assistant"
aria-label="Close Pulse Assistant backdrop"
/>
</Show>
<div class={rootClassName()} data-layout-mode={isOverlayLayout() ? 'overlay' : 'docked'}>
@@ -1161,14 +1162,19 @@ export const AIChat: Component<AIChatProps> = (props) => {
</button>
</Show>
{/* Header - wraps on mobile */}
<div class="flex flex-wrap items-center justify-between gap-2 px-4 py-3 border-b border-border bg-surface-alt">
<h2 class="text-sm font-semibold text-base-content">{AI_CHAT_DRAWER_TITLE}</h2>
<div class="flex flex-wrap items-center gap-2 px-4 py-3 border-b border-border bg-surface-alt">
<h2 class="min-w-0 flex-1 text-sm font-semibold text-base-content">
{AI_CHAT_DRAWER_TITLE}
</h2>
<div class="flex items-center gap-1.5">
<div
class="order-3 -mx-1 flex w-full min-w-0 items-center gap-1.5 overflow-x-auto px-1 pb-0.5 sm:order-none sm:mx-0 sm:w-auto sm:flex-none sm:overflow-visible sm:px-0 sm:pb-0"
data-testid="assistant-header-actions"
>
{/* New chat */}
<button
onClick={handleNewConversation}
class="flex items-center gap-1.5 px-2.5 py-1.5 text-[11px] text-muted hover:text-base-content rounded-md border border-border hover:border-border bg-surface transition-colors"
class="flex flex-shrink-0 items-center gap-1.5 px-2.5 py-1.5 text-[11px] text-muted hover:text-base-content rounded-md border border-border hover:border-border bg-surface transition-colors"
title={AI_CHAT_NEW_SESSION_BUTTON_TITLE}
>
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
@@ -1199,7 +1205,7 @@ export const AIChat: Component<AIChatProps> = (props) => {
<div class="relative" data-dropdown>
<button
onClick={() => setShowControlMenu(!showControlMenu())}
class={`flex items-center gap-1.5 px-2.5 py-1.5 text-[11px] font-medium rounded-md border transition-colors ${controlPresentation().pillClassName} ${controlSaving() ? 'opacity-70 cursor-wait' : 'hover:opacity-90'}`}
class={`flex flex-shrink-0 items-center gap-1.5 px-2.5 py-1.5 text-[11px] font-medium rounded-md border transition-colors ${controlPresentation().pillClassName} ${controlSaving() ? 'opacity-70 cursor-wait' : 'hover:opacity-90'}`}
title="Control mode"
disabled={controlSaving()}
>
@@ -1264,7 +1270,7 @@ export const AIChat: Component<AIChatProps> = (props) => {
onClick={() => {
void handleToggleSessions();
}}
class="p-2 hover:text-base-content rounded-md hover:bg-surface-hover transition-colors"
class="flex-shrink-0 p-2 hover:text-base-content rounded-md hover:bg-surface-hover transition-colors"
title={AI_CHAT_SESSION_MENU_TITLE}
>
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
@@ -1369,26 +1375,21 @@ export const AIChat: Component<AIChatProps> = (props) => {
</div>
</Show>
</div>
{/* Close button (Always visible as fallback) */}
<button
onClick={(e) => {
e.stopPropagation();
props.onClose();
}}
class="p-2 hover:text-base-content rounded-md hover:bg-surface-hover transition-colors"
title="Close panel"
>
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M6 18L18 6M6 6l12 12"
/>
</svg>
</button>
</div>
{/* Close button (Always visible as fallback) */}
<button
onClick={(e) => {
e.stopPropagation();
props.onClose();
}}
class="order-2 flex h-9 w-9 flex-shrink-0 items-center justify-center rounded-md hover:text-base-content hover:bg-surface-hover transition-colors sm:order-none"
title="Close panel"
aria-label="Close Pulse Assistant"
data-testid="assistant-close-button"
>
<XIcon class="h-5 w-5" />
</button>
</div>
<Show