# Atlas Widgets This document describes the reusable search widgets provided by Atlas, how they work, and how to use them safely. ## Scope These widgets are Django inclusion tags implemented in `atlas/templatetags/case_widgets.py` and rendered through partial templates in `atlas/templates/atlas/partials/`. Current widgets: - `case_search_widget` - `finding_search_widget` - `displayset_search_widget` They are HTMX-first widgets with JavaScript fallback to `fetch`. ## Load The Tags In any template that uses these widgets: ```django {% load case_widgets %} ``` ## 1) case_search_widget ### Tag Signature ```django {% case_search_widget collection=None cases=None recent_cases=None input_id='case-search-input' target_id='case-search-results' selection_mode='single' action_url=None show_select_button=False %} ``` ### Parameters - `collection`: Optional collection object. When set, search requests include `collection=` and rows render an `Add` action. - `cases`: Optional initial case queryset/list for first render. - `recent_cases`: Optional initial recent list. If omitted, widget auto-populates with up to 5 recent cases authored by the current user. - `input_id`: DOM id for the search input. Must be unique per page. - `target_id`: DOM id for results container. Must be unique per page. - `selection_mode`: Present in tag API but currently not used by rendering logic. - `action_url`: Present in tag API but currently not used by rendering logic. - `show_select_button`: When `True`, rows include a `Select` button and clicking rows emits selection events. ### How It Works - Input triggers search on: `keyup changed delay:400ms`. - HTMX call target: `atlas:case_search` (`/atlas/collection/case_search`). - The widget includes all local hidden state via: - `hx-include="closest .case-search-widget"` - Search results are swapped into the configured `target_id`. - If JavaScript cannot use HTMX, fallback `fetch` performs the same GET and swaps `innerHTML`. ### Events Emitted The widget dispatches a bubbling custom event from the widget container: - Event name: `case:selected` - Detail payload: - `casePk` (string) - `caseTitle` (string) Example listener: ```javascript document.body.addEventListener('case:selected', function (e) { const pk = e.detail && e.detail.casePk; const title = e.detail && e.detail.caseTitle; if (!pk) return; // consume selection }); ``` ### Result Row Contract Rows are rendered with: - `data-case-pk` - visible title in `h6` Actions per mode: - `collection` provided: `Add` form + `View` - no collection, `show_select_button=True`: `Select` + `View` - no collection, `show_select_button=False`: `View` ### Common Usage Patterns #### A. Case picker behavior (select destination/import target) ```django {% case_search_widget collection=None input_id='move-series-case-search-input' target_id='move-series-case-search-results' show_select_button=True %} ``` Then consume `case:selected` and write `casePk` to your hidden form field. #### B. Add to collection flow ```django {% case_search_widget collection=collection cases=cases recent_cases=recent_cases input_id='case-search-input-collection-'|add:collection.pk|stringformat:'s' target_id='case-search-results-collection-'|add:collection.pk|stringformat:'s' %} ``` No event handling is required; rows provide server-posted `Add` action. ### Endpoint Notes The widget uses `atlas:case_search` for HTMX/fetch updates. That view supports: - `q` search text - `collection` optional collection id - `show_select` optional flag to preserve select-button rendering on refresh ## 2) finding_search_widget ### Tag Signature ```django {% finding_search_widget case=None findings=None input_id='finding-search-input' target_id='finding-search-results' %} ``` ### Parameters - `case`: Optional case instance; when present, search is filtered to findings linked to series in that case. - `findings`: Optional initial findings list. - `input_id`, `target_id`: DOM ids. Must be unique per page. ### How It Works - HTMX endpoint: `atlas:finding_search_partial` / `atlas:finding_search`. - Search query is sent as `q`. - Case filter (if set) is sent as `case=`. - Widget shows a case filter badge and a clear (`x`) button. - Script mirrors active case into dataset fields used by markup insertion workflows: - `input.dataset.currentCase` - nearby `textarea.dataset.currentCase` ### Result Row Contract Rows are rendered with: - `data-finding-pk` - `data-seriesfinding-pk` - `data-label` These attributes are consumed by `atlas/static/atlas/js/markup_inserter.js` to insert tokens like: - `[[seriesfinding:|