diff --git a/web/src/components/SessionList.directory-action.test.tsx b/web/src/components/SessionList.directory-action.test.tsx index 7e399cbb..a1397457 100644 --- a/web/src/components/SessionList.directory-action.test.tsx +++ b/web/src/components/SessionList.directory-action.test.tsx @@ -129,7 +129,8 @@ describe('SessionList directory action', () => { fireEvent.click(screen.getByRole('button', { name: SEARCH_LABEL })) const searchInput = screen.getByPlaceholderText(SEARCH_PLACEHOLDER) - const headerRow = searchInput.parentElement?.parentElement + // input → field row → session-list-search → header actions row + const headerRow = searchInput.parentElement?.parentElement?.parentElement expect(headerRow).toHaveClass('px-2') expect(headerRow).toHaveClass('py-1') }) @@ -194,9 +195,10 @@ describe('SessionList time filter', () => { const searchButton = screen.getByRole('button', { name: SEARCH_LABEL }) const filterButton = screen.getByRole('button', { name: 'Filter sessions by last activity' }) const searchControl = searchButton.parentElement - expect(searchControl?.nextElementSibling).toBe(filterButton) - expect(searchControl?.parentElement).toBe(filterButton.parentElement) - expect(searchControl?.parentElement).toHaveClass('relative', 'gap-1') + expect(searchControl?.nextElementSibling).toBe(filterButton.parentElement) + expect(searchControl?.parentElement).toBe(filterButton.parentElement?.parentElement) + expect(searchControl?.parentElement).toHaveClass('relative', 'gap-1', 'min-w-0') + expect(searchControl?.parentElement?.className.split(/\s+/)).not.toContain('shrink-0') expect(screen.queryByPlaceholderText(SEARCH_PLACEHOLDER)).toBeNull() fireEvent.click(filterButton) @@ -211,8 +213,8 @@ describe('SessionList time filter', () => { expect(screen.getByRole('button', { name: /Recent session/ })).toBeInTheDocument() expect(screen.queryByRole('button', { name: /Old session/ })).toBeNull() expect(screen.queryByPlaceholderText(SEARCH_PLACEHOLDER)).toBeNull() - expect(filterButton).toHaveAttribute('title', '2026-07-17 – 2026-07-18') - expect(filterButton).toHaveAccessibleName('Filter sessions by last activity: 2026-07-17 – 2026-07-18') + expect(filterButton).toHaveAttribute('title', '2026-07-17 - 2026-07-18') + expect(filterButton).toHaveAccessibleName('Filter sessions by last activity: 2026-07-17 - 2026-07-18') expect(filterButton).toHaveFocus() }) @@ -277,7 +279,7 @@ describe('SessionList time filter', () => { fireEvent.click(screen.getByRole('button', { name: `${new Date(2026, 6, 18).toLocaleDateString()}, has session activity` })) expect(filterButton).toHaveAttribute('aria-expanded', 'false') - expect(filterButton).toHaveAttribute('title', '2026-07-01 – 2026-07-18') + expect(filterButton).toHaveAttribute('title', '2026-07-01 - 2026-07-18') }) it('returns focus to the search input after clearing the date range', () => { @@ -1091,6 +1093,45 @@ describe('SessionList search toggle', () => { expect(screen.getByRole('button', { name: /Other task/ })).toBeInTheDocument() }) + it('gives expanded search a 48px clear end-cap with calendar immediately left (tiann/hapi#2019)', () => { + renderWithProviders( + + ) + + fireEvent.click(screen.getByRole('button', { name: SEARCH_LABEL })) + const input = screen.getByPlaceholderText(SEARCH_PLACEHOLDER) + fireEvent.change(input, { target: { value: 'DJ' } }) + + const clearButton = screen.getByRole('button', { name: 'Clear search' }) + expect(clearButton.className).toMatch(/session-list-search-clear-endcap/) + expect(clearButton.className).toMatch(/min-h-\[48px\]/) + expect(clearButton.className).toMatch(/min-w-\[48px\]/) + + const dateButton = screen.getByTestId('session-list-search-date-embedded') + expect(dateButton.className).toMatch(/min-h-\[48px\]/) + expect(dateButton.className).toMatch(/min-w-\[48px\]/) + expect(clearButton.parentElement?.contains(dateButton)).toBe(true) + + const fieldChildren = Array.from(clearButton.parentElement?.children ?? []) + const dateIndex = fieldChildren.findIndex((node) => node === dateButton.parentElement || node.contains(dateButton)) + const clearIndex = fieldChildren.indexOf(clearButton) + expect(dateIndex).toBeGreaterThanOrEqual(0) + expect(clearIndex).toBeGreaterThan(dateIndex) + }) + it('stays expanded with focus on the input after clearing the query', () => { renderWithProviders( { - const returnFocus = () => { - (variant === 'embedded' ? inputRef.current : dateButtonRef.current)?.focus() + const returnDateFocus = () => { + if (props.expanded) { + inputRef.current?.focus() + return } - - return ( - <> - - {datePickerOpen ? ( - <> - + + ) + + const datePicker = datePickerOpen ? ( + <> + ) : null} - {renderDateFilter('standalone')} + {renderDateButton('standalone')} + {datePicker} ) } return (
{ if (!event.currentTarget.contains(event.relatedTarget as Node | null)) { @@ -892,40 +925,39 @@ export function SessionListSearch(props: { } }} > -
- -
- props.onChange(event.target.value)} - placeholder={t('sessions.search.placeholder')} - aria-label={searchLabel} - title={searchLabel} - className={cn( - 'w-full appearance-none rounded-lg border border-[var(--app-border)] bg-[var(--app-bg)] py-1.5 pl-8 text-sm text-[var(--app-fg)] outline-none transition-colors placeholder:text-[var(--app-hint)] [text-overflow:ellipsis] focus:border-[var(--app-link)] [&::-webkit-search-cancel-button]:hidden [&::-webkit-search-decoration]:hidden', - props.value ? 'pr-16' : 'pr-7' - )} - /> - {props.value ? ( - - ) : null} -
- {renderDateFilter('embedded')} +
+
+ +
+ props.onChange(event.target.value)} + placeholder={t('sessions.search.placeholder')} + aria-label={searchLabel} + title={searchLabel} + className="min-w-0 flex-1 appearance-none bg-transparent px-2 py-1.5 text-sm text-[var(--app-fg)] outline-none placeholder:text-[var(--app-hint)] [text-overflow:ellipsis] [&::-webkit-search-cancel-button]:hidden [&::-webkit-search-decoration]:hidden" + /> + {renderDateButton('embedded')} + {props.value ? ( + + ) : null}
+ {datePicker}
) }