7.4 KiB
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_widgetfinding_search_widgetdisplayset_search_widget
They are HTMX-first widgets with JavaScript fallback to fetch.
Load The Tags
In any template that uses these widgets:
{% load case_widgets %}
1) case_search_widget
Tag Signature
{% 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 includecollection=<pk>and rows render anAddaction.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: WhenTrue, rows include aSelectbutton 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
fetchperforms the same GET and swapsinnerHTML.
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:
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:
collectionprovided:Addform +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)
{% 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
{% 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:
qsearch textcollectionoptional collection idshow_selectoptional flag to preserve select-button rendering on refresh
2) finding_search_widget
Tag Signature
{% 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=<pk>. - 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-pkdata-seriesfinding-pkdata-label
These attributes are consumed by atlas/static/atlas/js/markup_inserter.js to insert tokens like:
[[seriesfinding:<pk>|<label>]][[finding:<pk>|<label>]]
3) displayset_search_widget
Tag Signature
{% displayset_search_widget
case=None
displaysets=None
input_id='displayset-search-input'
target_id='displayset-search-results'
%}
Parameters
case: Optional case instance; when present, search is filtered to display sets in that case.displaysets: Optional initial list.input_id,target_id: DOM ids. Must be unique per page.
How It Works
- HTMX endpoint:
atlas:displayset_search_partial/atlas:displayset_search. - Query sent as
q; case scoping sent ascase=<pk>. - Includes case badge + clear button behavior similar to finding widget.
- Also mirrors case context into input/textarea datasets for markup insertion workflows.
Result Row Contract
Rows are rendered with:
data-ds-pkdata-label
Used by atlas/static/atlas/js/markup_inserter.js to insert:
[[displayset:<pk>|<label>]]
Integration Guidelines
- Keep each widget instance ids unique:
- unique
input_id - unique
target_id
- unique
- Prefer one event listener per feature container or page for
case:selected. - Do not depend on button text (
Select,View) in JS. Use data attributes and events. - If your workflow needs selection, set
show_select_button=Trueoncase_search_widget. - If your workflow is add-to-collection, pass
collectionand rely on rowAddforms.
Known Constraints
selection_modeandaction_urlare currently API placeholders forcase_search_widget; they are not applied by templates/views yet.- Widget scripts are inline with each widget include. If many instances are rendered, listeners are instance-scoped but still duplicated per include.
Quick Troubleshooting
- No results update:
- verify
input_idandtarget_idare unique and present in DOM. - verify endpoint routes exist and user is authenticated.
- verify
- Selection not received:
- ensure
show_select_button=Trueand listener is bound (for example ondocument.body). - inspect
case:selectedpayload in DevTools.
- ensure
- Case scoping not applied in finding/displayset widgets:
- ensure
caseis passed to the tag. - verify
case=<pk>appears in network requests.
- ensure
Source Files
atlas/templatetags/case_widgets.pyatlas/templates/atlas/partials/case_search_widget.htmlatlas/templates/atlas/partials/case_search_results.htmlatlas/templates/atlas/partials/_case_search_item.htmlatlas/templates/atlas/partials/finding_search_widget.htmlatlas/templates/atlas/partials/_finding_search_results.htmlatlas/templates/atlas/partials/displayset_search_widget.htmlatlas/templates/atlas/partials/_displayset_search_results.htmlatlas/views.py(case_search,finding_search,displayset_search)atlas/static/atlas/js/markup_inserter.js