rendering

Queue panel and item rendering with callback-based custom content.

source

render_queue_item


def render_queue_item(
    config:SortableQueueConfig, # Queue configuration
    ids:SortableQueueHtmlIds, # HTML ID generators
    urls:SortableQueueUrls, # URL endpoints
    item:dict, # Queue item data
    index:int, # 0-based position in queue
    render_content:Callable, # Callback for custom item content
    extra_attrs:Optional=None, # Additional HTML attributes per item
)->Any: # Li element

Render a single queue item with drag handle, position, custom content, and remove button.


source

render_sortable_queue


def render_sortable_queue(
    config:SortableQueueConfig, # Queue configuration
    ids:SortableQueueHtmlIds, # HTML ID generators
    urls:SortableQueueUrls, # URL endpoints
    queue_items:List, # Ordered list of queue item dicts
    render_content:Callable, # Callback for custom item content
    render_empty:Optional=None, # Custom empty state (default provided)
    render_header_actions:Optional=None, # Custom header actions
    render_footer:Optional=None, # Optional footer content
    extra_item_attrs:Optional=None, # Additional data-* attributes per item
    container_classes:tuple=(), # Additional classes on container div
)->Any: # Queue panel element

Render the complete sortable queue panel.

Tests

from fasthtml.common import to_xml

# Test setup
config = SortableQueueConfig(prefix="test")
ids_obj = SortableQueueHtmlIds(prefix="test")
urls = SortableQueueUrls(reorder="/q/reorder", remove="/q/remove", clear="/q/clear")
test_items = [{"id": "a", "name": "Alpha"}, {"id": "b", "name": "Beta"}]

def test_render_content(item, index):
    return Span(item["name"], cls="grow")

# --- render_queue_item ---
li_xml = to_xml(render_queue_item(config, ids_obj, urls, test_items[0], 0, test_render_content))
assert 'id="test-queue-item-a"' in li_xml
assert 'name="item"' in li_xml and 'value="a"' in li_xml  # Hidden input
assert "drag-handle" in li_xml  # Drag handle class
assert "1." in li_xml  # 1-based position
assert "Alpha" in li_xml  # Custom content
assert "/q/remove" in li_xml  # Remove button target
assert 'queue-item' in li_xml  # Item class

# --- render_sortable_queue with items ---
panel_xml = to_xml(render_sortable_queue(config, ids_obj, urls, test_items, test_render_content))
assert 'id="test-queue-container"' in panel_xml
assert 'id="test-queue-list"' in panel_xml
assert 'hx-trigger="end"' in panel_xml
assert 'hx-include="this"' in panel_xml  # HTMX 2.x requirement
assert "Selected" in panel_xml  # Title
assert "/q/reorder" in panel_xml  # Reorder URL
assert "/q/clear" in panel_xml  # Clear button
assert "Alpha" in panel_xml and "Beta" in panel_xml  # Both items rendered
assert "sortable" in panel_xml  # Sortable class on ul

# --- render_sortable_queue empty state ---
empty_xml = to_xml(render_sortable_queue(config, ids_obj, urls, [], test_render_content))
assert 'id="test-queue-container"' in empty_xml
assert 'id="test-queue-empty"' in empty_xml
assert "No items selected" in empty_xml
assert "test-queue-list" not in empty_xml  # No list when empty
assert "/q/clear" not in empty_xml  # No clear button when empty

# --- Custom empty state ---
def custom_empty():
    return P("Nothing here!")

custom_empty_xml = to_xml(render_sortable_queue(config, ids_obj, urls, [], test_render_content, render_empty=custom_empty))
assert "Nothing here!" in custom_empty_xml

# --- Custom footer ---
def test_footer(items_list):
    return Div(f"{len(items_list)} items total", id="footer")

footer_xml = to_xml(render_sortable_queue(config, ids_obj, urls, test_items, test_render_content, render_footer=test_footer))
assert "2 items total" in footer_xml

print("All rendering tests passed")
All rendering tests passed