Self-contained keyboard system for sortable queue navigation and actions.
create_queue_keyboard_system
def create_queue_keyboard_system( config:SortableQueueConfig, # Queue configuration ids:SortableQueueHtmlIds, # HTML ID generators urls:SortableQueueUrls, # URL endpoints for HTMX actions zone_focus_classes:tuple=('ring-0',), # CSS classes when queue zone is active item_focus_classes:Optional=None, # CSS classes on focused item (default: bg-base-300) data_attributes:tuple=(), # Data attributes to extract (e.g., ("record-id", "provider-id")) on_focus_change:Optional=None, # JS callback on item focus change hidden_input_prefix:Optional=None, # Prefix for hidden state inputs system_id:Optional=None, # Keyboard system ID (auto-generated from ids.system_id if not set) manager_label:Optional=None, # Human-readable label for the underlying ZoneManager (used by render_keyboard_hints_modal section header when this system is rendered as a child) show_hints:bool=False, # Show keyboard hints UI)->KeyboardSystem: # Complete rendered keyboard system
Create a self-contained keyboard system for the sortable queue.
Returns a rendered KeyboardSystem with a single FocusZone (LinearVertical navigation) and built-in actions for Delete/Backspace remove and Shift+Arrow reorder. Works standalone or as a child in a hierarchy via coord.setParent(system_id, parent_id).
Pass manager_label (e.g., “Selection Queue”) so that when this system is rendered as a child_managers entry in render_keyboard_hints_modal, the modal’s section header reads as the label instead of falling back to the technical system_id. Access the underlying ZoneManager via the returned KeyboardSystem.manager field for the child_managers handoff.
Tests
from fasthtml.common import to_xml# Test setupconfig = SortableQueueConfig(prefix="tk")ids = SortableQueueHtmlIds(prefix="tk")urls = SortableQueueUrls(reorder="/tk/reorder", remove="/tk/remove", clear="/tk/clear")# --- Basic creation with defaults ---kb = create_queue_keyboard_system(config, ids, urls)assertisinstance(kb, KeyboardSystem)assert kb.script isnotNoneassert kb.hidden_inputs isnotNoneassert kb.action_buttons isnotNone# --- Default item focus classes (bg-base-300) ---script_xml = to_xml(kb.script)assert"bg-base-300"in script_xml # Default focus styling in JS config# --- System ID ---assert"tk-queue-kb"in script_xml # system_id appears in JS config# Custom system_idkb2 = create_queue_keyboard_system(config, ids, urls, system_id="my-custom-kb")script_xml2 = to_xml(kb2.script)assert"my-custom-kb"in script_xml2# --- Action buttons ---btns_xml = to_xml(kb.action_buttons)assert ids.remove_btn in btns_xml # Remove button presentassert ids.reorder_up_btn in btns_xml # Reorder up button presentassert ids.reorder_down_btn in btns_xml # Reorder down button presentassert"/tk/remove"in btns_xml # Remove URL wiredassert"/tk/reorder"in btns_xml # Reorder URL wired# --- Direction vals on reorder buttons ---assert"direction"in btns_xml# --- Zone config in script ---assertf"li.{config.item_class}"in script_xml # Item selectorassert ids.container in script_xml # Zone container ID# --- Custom item focus classes override default ---kb3 = create_queue_keyboard_system( config, ids, urls, item_focus_classes=("bg-custom",), data_attributes=("record-id", "provider-id"), on_focus_change="myFocusCallback", hidden_input_prefix="my-focused", show_hints=True,)assertisinstance(kb3, KeyboardSystem)assert kb3.hints isnotNone# Hints rendered when show_hints=Truescript_xml3 = to_xml(kb3.script)assert"bg-custom"in script_xml3assert"bg-base-300"notin script_xml3 # Default overriddenassert"myFocusCallback"in script_xml3assert"record-id"in script_xml3print("All keyboard tests passed")
All keyboard tests passed
# Tests for manager_label + KeyboardSystem.manager handoff# The L5 contract: create_queue_keyboard_system must expose the underlying# ZoneManager so consumers can hand it to render_keyboard_hints_modal as a# child_managers entry. Manager-label must be settable for human-readable# section headers in the modal.# Default: manager exists, label is None (falls back to system_id when rendered)kb_default = create_queue_keyboard_system(config, ids, urls)assert kb_default.manager isnotNone, "KeyboardSystem.manager must be populated by render_keyboard_system"assert kb_default.manager.label isNone, "Default manager_label is None — modal falls back to system_id"assert kb_default.manager.get_display_label() == ids.system_id, \"When label is None, get_display_label must fall back to the ZoneManager's system_id"# With manager_label: ZoneManager carries it and get_display_label returns itkb_labeled = create_queue_keyboard_system(config, ids, urls, manager_label="Selection Queue")assert kb_labeled.manager.label =="Selection Queue"assert kb_labeled.manager.get_display_label() =="Selection Queue", \"When label is set, get_display_label must return the label (not system_id)"# manager_label is render-only — does NOT appear in JS configimport jsonscript_xml_labeled = to_xml(kb_labeled.script)# Label should not be serialized into the JS-side cfgassert'"label":'notin script_xml_labeled, \"manager_label is Python-render-only; must not leak into the JS config"# Sanity check: manager_label + system_id can co-existkb_both = create_queue_keyboard_system( config, ids, urls, system_id="custom-queue-kb", manager_label="Sortable Queue",)assert kb_both.manager.system_id =="custom-queue-kb"assert kb_both.manager.label =="Sortable Queue"assert kb_both.manager.get_display_label() =="Sortable Queue"# label winsprint("manager_label + KeyboardSystem.manager handoff tests passed")