Skip to content

Search UX v2 #1: chip infrastructure#653

Draft
hoyla wants to merge 10 commits intomainfrom
ljh-searchux-v2-pr1-chip-infrastructure
Draft

Search UX v2 #1: chip infrastructure#653
hoyla wants to merge 10 commits intomainfrom
ljh-searchux-v2-pr1-chip-infrastructure

Conversation

@hoyla
Copy link
Copy Markdown
Contributor

@hoyla hoyla commented Apr 1, 2026

Lays the groundwork for the search UX improvements epic #633.

Closes #634. Incidentally fixes #477, not that that matters much since we move chips out of the search bar in a subsequent branch.

Important

This is the first of five PRs in the Search UX Improvements epic. All other PRs depend on this one — it must be merged first.

PR1 Chip parsing infrastructure  ← you are here
 └─ PR2 Shared utilities & Downshift dependency
     └─ PR3 Active filter chips & multi-select
         └─ PR4 Search page & AddFilterModal
             └─ PR5 Sidebar tri-state filters

Changes to the user experience

This PR is primarily infrastructure, but it does include a few visible changes:

Search bar chip improvements

  • Long chip values are truncated — chip values longer than 75 characters are shortened to 72 characters with ..., and hovering shows the full value in a tooltip. This prevents the search bar from being stretched by very long folder paths or search terms.
  • Chip name labels are truncated — chip name labels (e.g. "Workspace Folder") are capped at 150px with ellipsis overflow, preventing excessively wide chips.
  • Search bar overflow handling — the inline input now respects max-width: 100% with text overflow ellipsis, and the last chip element always reserves at least 100px for typing.

Before:
Picture 284

After:
Picture 285

Having truncation rules will be useful even when we move chips out of the searchbox.

New search chip types

  • Language chip — a new "Language" dropdown chip is available in the search bar, allowing users to filter by document language (Arabic, English, French, German, Persian, Portuguese, Russian, Spanish). Selecting multiple languages works correctly with the multi-word template expansion fix.
  • Transcription option for Has Field — the existing "Has Field" dropdown chip now includes a "Transcription" option alongside OCR, Text, and Author.

Backend: exclude filters

  • The search API now accepts workspace_exclude[] and ingestion_exclude[] query parameters, allowing results from specific workspaces or datasets to be excluded. This is plumbing for the tri-state sidebar filters in PR5 — there is no UI to trigger these yet.

Bug fix

  • InputSupper focus crash — fixed a pre-existing bug where pressing the search keyboard shortcut before the search box was fully initialised would crash with Cannot read properties of undefined (reading 'focus').

Notable non-change

  • The "Search workspace" and "Search in folder" buttons continue to work exactly as before — navigating to the search page with the workspace ticked in the sidebar. The way this is achieved will change in a subsequent PR to make it more consistent with chip-based query params.

What's included

  • Chip parsing and serialisation (chipParsing.ts) — parses q strings into typed chips and rebuilds them. Three chip kinds are supported via a discriminated union: SingleChip, MultiChip, and DateRangeChip.
  • Shared chip constants (chipNames.ts) — all magic strings for chip names, types, and kinds live here so typos cause import errors rather than silent runtime bugs.
  • File type category mapping (fileTypeCategories.ts) — maps MIME types to user-friendly categories (PDF, Images, Spreadsheets, etc.) for the File Type filter in subsequent PRs.
  • Backend query construction helpersbuildWorkspaceSearchQ and buildWorkspaceFolderSearchQ centralise the construction of search queries for workspace navigation, replacing scattered inline logic.
  • isRawChip type guard — replaces repeated inline type assertions with a single reusable guard for narrowing raw parsed chips.
  • Workspace search button updates — workspace search buttons now use chip-based query parameters rather than manually constructed strings.
  • SCSS improvements — shared SCSS variables for chip styling and improved truncation behaviour in the InputSupper component.

TypeScript

All new code is written in TypeScript. Existing chip-related JS files have been converted to .ts/.tsx with full type annotations and discriminated union types for compile-time safety.

Automated tests

Chip parsing logic is covered by unit tests in chipParsing.spec.ts. File type category mapping is covered in fileTypeCategories.spec.ts. Backend chip expansion is covered in ChipsTest.scala.

Manual tests

  • Tested locally.
  • Tested on playground.

@hoyla hoyla added the feature Departmental tracking: work on a new feature label Apr 1, 2026
hoyla added 10 commits April 9, 2026 10:46
…port

Add Language and Transcription chip types to Chip.scala.
Add toBackendQ parsing in SearchContext to convert chip-encoded q
parameters into Elasticsearch queries with proper MIME type quoting
and OR semantics for multi-value chips.
Add comprehensive ChipsTest covering single/multi-value, date range,
negation, and operator expansion scenarios.
Introduce chipNames.js with constants for all filter chip types and
kinds (single, multi, date-range).

Add chipParsing.js with parseChips() to extract structured filter
chips from JSON-encoded q strings, rebuildQ() to serialize them back,
and toBackendQ() to convert to Elasticsearch query syntax.

Add fileTypeCategories.js mapping MIME types to human-friendly
categories (PDF, Images, Spreadsheets, etc.) with memoized lookups.

Include comprehensive test suitesInclude comprehensive test suitesInclude compred file type category mapping.
Update InputSupper Chip component with character-budget truncation
and overflow handling. Add shared SCSS variables for filter chip
colours. Import new stylesheet partials in main.scss.
Convert workspace search URL construction from the old filter-based
format to the new JSON-encoded chip format, ensuring workspace
searches produce chip-decorated queries.
Convert chipNames.js, chipParsing.js, and fileTypeCategories.js (plus their
spec files) from JavaScript to TypeScript with proper type annotations.

Add discriminated union types for chips (SingleChip, MultiChip, DateRangeChip),
typed interfaces for parsed results, raw backend chips, suggested fields,
polarity values, and chip filters.
- Default chipType to 'text' when element.t is undefined in chipParsing.ts
- Add proper type annotations to spec helpers (q, chip functions)
- Add Chip[] type annotations to const arrays passed to rebuildQ
- Use type assertions for discriminated union property access (.values,
  .from, .to, .options)
- Add non-null assertions for .find() results and toBackendQ returns
- Add explicit 'any' annotations on JSON.parse callback parameters
- Remove premature SCSS imports for _active-filters and _add-filter-modal
  (files don't exist yet)
Replace inline chip JSON in WorkspaceSummary and Workspaces with
buildWorkspaceSearchQ and buildWorkspaceFolderSearchQ helpers from
chipParsing.ts. This keeps the wire format defined in one place
rather than duplicated at each call site.
Add isRawChip type guard function to replace ~20 instances of
_isObject(el) && (el as RawChip).n patterns throughout chipParsing.ts.
This narrows the type once at the guard, eliminating redundant casts
and making the code shorter and safer.
- Guard InputSupper.focus() and select() with optional chaining to prevent
  crash when currentRef is undefined (pre-existing bug surfaced by keyboard
  shortcut before elements are initialised)

- Revert WorkspaceSummary 'Search workspace' button to use sidebar filters
  (workspace URL param) instead of embedding workspace in q as a chip,
  since the chip-based search API integration is not yet wired up (PR3)

- Restore workspace filter param in Workspaces 'Search in folder' context
  menu so the sidebar correctly ticks the workspace being searched
Always expand the template per-value when a chip value contains OR,
regardless of how many _word_ placeholders the template has. Previously
only multi-placeholder templates were expanded, meaning a Has Field chip
with 'ocr OR transcript' would produce the invalid Elasticsearch query
_exists_:(ocr OR transcript) instead of (_exists_:(ocr) OR _exists_:(transcript)).

Update tests to reflect the corrected behaviour.
@hoyla hoyla force-pushed the ljh-searchux-v2-pr1-chip-infrastructure branch from 8bc39c8 to feb167c Compare April 9, 2026 09:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature Departmental tracking: work on a new feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Giant search UX 1: infrastructure for a richer chip implementation Giant 'search in folder': truncate very long folder names; ditch asterisk

1 participant