# Test render_keyboard_system
from fasthtml.common import to_xml
from cjm_fasthtml_keyboard_navigation.core.focus_zone import FocusZone
from cjm_fasthtml_keyboard_navigation.core.actions import KeyAction
browser = FocusZone(
id="browser",
item_selector="tr.item",
data_attributes=("job-id",)
)
queue = FocusZone(
id="queue",
item_selector="li.item"
)
manager = ZoneManager(
zones=(browser, queue),
actions=(
KeyAction(key=" ", htmx_trigger="toggle-btn", description="Select"),
KeyAction(key="Delete", htmx_trigger="delete-btn", description="Remove"),
)
)
system = render_keyboard_system(
manager,
url_map={
"toggle-btn": "/toggle",
"delete-btn": "/delete"
},
target_map={
"toggle-btn": "#list",
"delete-btn": "#list"
}
)
assert isinstance(system, KeyboardSystem)
assert system.hints is not None
# Check script
script_html = to_xml(system.script)
assert "handleKeydown" in script_html
# Check hidden inputs
inputs_html = to_xml(system.hidden_inputs)
assert 'id="browser-job-id"' in inputs_html
# Check action buttons - no hx-trigger by default (JS handles triggering)
buttons_html = to_xml(system.action_buttons)
assert 'id="toggle-btn"' in buttons_html
assert 'id="delete-btn"' in buttons_html
assert 'hx-trigger' not in buttons_html # JavaScript handles triggering
# Check hints
hints_html = to_xml(system.hints)
assert "Select" in hints_htmlKeyboard System
High-level API for rendering complete keyboard navigation systems.
KeyboardSystem Result
KeyboardSystem
def KeyboardSystem(
script:Script, hidden_inputs:Div, action_buttons:Div, hints:Optional[Div]=None
)->None:
Container for all keyboard navigation components.
Render Keyboard System
The main entry point for generating keyboard navigation components.
If include_map is not provided, it will be auto-generated based on the actions: - Actions with zone_ids will include only those zones’ hidden inputs - Actions without zone_ids will include all zones’ hidden inputs
This ensures HTMX requests automatically include the focused item’s data attributes.
render_keyboard_system
def render_keyboard_system(
manager:ZoneManager, # the zone manager configuration
url_map:dict[str, str], # action button ID -> URL
target_map:dict[str, str], # action button ID -> target selector
include_map:dict[str, str] | None=None, # action button ID -> include selector (auto-generated if None)
swap_map:dict[str, str] | None=None, # action button ID -> swap value
vals_map:dict[str, dict] | None=None, # action button ID -> hx-vals dict
show_hints:bool=True, # render keyboard hints UI
hints_badge_style:str='ghost', # badge style for hints
include_state_inputs:bool=False, # include state tracking inputs
)->KeyboardSystem: # complete keyboard system
Render complete keyboard navigation system.
# Test without hints
system_no_hints = render_keyboard_system(
manager,
url_map={"toggle-btn": "/toggle", "delete-btn": "/delete"},
target_map={"toggle-btn": "#list", "delete-btn": "#list"},
show_hints=False
)
assert system_no_hints.hints is None
assert len(system_no_hints.all_components()) == 3 # No hints# Test with vals_map for direction parameters
manager_with_reorder = ZoneManager(
zones=(browser, queue),
actions=(
KeyAction(key="ArrowUp", modifiers=frozenset({"shift"}), htmx_trigger="reorder-up-btn"),
KeyAction(key="ArrowDown", modifiers=frozenset({"shift"}), htmx_trigger="reorder-down-btn"),
)
)
system_with_vals = render_keyboard_system(
manager_with_reorder,
url_map={
"reorder-up-btn": "/reorder",
"reorder-down-btn": "/reorder"
},
target_map={
"reorder-up-btn": "#queue",
"reorder-down-btn": "#queue"
},
vals_map={
"reorder-up-btn": {"direction": "up"},
"reorder-down-btn": {"direction": "down"}
},
show_hints=False
)
buttons_html = to_xml(system_with_vals.action_buttons)
assert '"direction": "up"' in buttons_html
assert '"direction": "down"' in buttons_html# Test all_components unpacking
components = system.all_components()
assert len(components) == 4 # script, inputs, buttons, hintsQuick Setup Function
For simpler use cases with sensible defaults.
quick_keyboard_system
def quick_keyboard_system(
zones:tuple[FocusZone, ...], # focus zones
actions:tuple[KeyAction, ...], # keyboard actions
url_map:dict[str, str], # action URLs
target_map:dict[str, str], # action targets
kwargs:VAR_KEYWORD
)->KeyboardSystem: # complete keyboard system
Quick setup for simple keyboard navigation.
# Test quick setup
system = quick_keyboard_system(
zones=(browser,),
actions=(
KeyAction(key=" ", htmx_trigger="select-btn", description="Select"),
),
url_map={"select-btn": "/select"},
target_map={"select-btn": "#list"},
show_hints=False
)
assert isinstance(system, KeyboardSystem)
assert system.hints is None