Table headers and column options visible for 0 rows

Closes #2701
This commit is contained in:
Simon Willison 2026-04-22 22:22:47 -07:00
commit 0dc7bb19d9
4 changed files with 28 additions and 7 deletions

View file

@ -1,6 +1,6 @@
<!-- above-table-panel is a hook node for plugins to attach to . Displays even if no data available -->
<div class="above-table-panel"> </div>
{% if display_rows %}
{% if display_columns %}
<div class="table-wrapper">
<table class="rows-and-columns">
<thead>
@ -31,6 +31,7 @@
</tbody>
</table>
</div>
{% else %}
{% endif %}
{% if not display_rows %}
<p class="zero-results">0 records</p>
{% endif %}

View file

@ -141,7 +141,6 @@
{% if all_columns %}
<column-chooser></column-chooser>
<button class="choose-columns-mobile small-screen-only" onclick="openColumnChooser()">Choose columns</button>
{% if display_rows %}
<button type="button" class="column-actions-mobile small-screen-only">
<svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="3"></circle>
@ -149,7 +148,6 @@
</svg>
<span>Column actions</span>
</button>
{% endif %}
<script>
window._columnChooserData = {{ {"allColumns": all_columns, "selectedColumns": display_columns|map(attribute='name')|list, "primaryKeys": primary_keys}|tojson }};
</script>

View file

@ -805,7 +805,7 @@ async def test_blob_download_invalid_messages(ds_client, path, expected_message)
async def test_zero_results(ds_client, path):
response = await ds_client.get(path)
soup = Soup(response.text, "html.parser")
assert 0 == len(soup.select("table"))
assert 0 == len(soup.select("table tbody tr"))
assert 1 == len(soup.select("p.zero-results"))

View file

@ -752,8 +752,11 @@ async def test_column_chooser_present(ds_client):
@pytest.mark.asyncio
async def test_mobile_column_actions_present(ds_client):
response = await ds_client.get("/fixtures/facetable")
@pytest.mark.parametrize(
"path", ["/fixtures/facetable", "/fixtures/123_starts_with_digits"]
)
async def test_mobile_column_actions_present(ds_client, path):
response = await ds_client.get(path)
assert response.status_code == 200
soup = Soup(response.text, "html.parser")
button = soup.select_one("button.column-actions-mobile.small-screen-only")
@ -764,6 +767,25 @@ async def test_mobile_column_actions_present(ds_client):
"mobile-column-actions.js" in (script.get("src") or "")
for script in soup.find_all("script")
)
# mobile-column-actions.js builds its dialog from <th data-column> elements,
# so the thead must render even when the table has no rows.
ths = soup.select("table.rows-and-columns thead th[data-column]")
assert len(ths) >= 1
@pytest.mark.asyncio
async def test_zero_row_table_renders_thead(ds_client):
response = await ds_client.get("/fixtures/123_starts_with_digits")
assert response.status_code == 200
soup = Soup(response.text, "html.parser")
table = soup.select_one("table.rows-and-columns")
assert table is not None
column_names = [
th.get("data-column") for th in table.select("thead th[data-column]")
]
assert "content" in column_names
assert table.select_one("tbody tr") is None
assert soup.select_one("p.zero-results") is not None
@pytest.mark.asyncio