diff --git a/datasette/facets.py b/datasette/facets.py
index ff6396d726..9d95d0f378 100644
--- a/datasette/facets.py
+++ b/datasette/facets.py
@@ -105,7 +105,9 @@ def get_facet_size(self):
facet_size = self.ds.setting("default_facet_size")
max_returned_rows = self.ds.setting("max_returned_rows")
custom_facet_size = self.request.args.get("_facet_size")
- if custom_facet_size and custom_facet_size.isdigit():
+ if custom_facet_size == "max":
+ facet_size = max_returned_rows
+ elif custom_facet_size and custom_facet_size.isdigit():
facet_size = int(custom_facet_size)
return min(facet_size, max_returned_rows)
diff --git a/datasette/static/app.css b/datasette/static/app.css
index 617bd2b15b..7f04a16225 100644
--- a/datasette/static/app.css
+++ b/datasette/static/app.css
@@ -645,13 +645,20 @@ form button[type=button] {
.facet-info a.cross:active {
text-decoration: none;
}
+ul li.facet-truncated {
+ list-style-type: none;
+ position: relative;
+ top: -0.35em;
+ text-indent: 0.85em;
+}
+
.advanced-export {
margin-top: 1em;
padding: 0.01em 2em 0.01em 1em;
width: auto;
display: inline-block;
box-shadow: 1px 2px 8px 2px rgba(0,0,0,0.08);
- background-color: white;
+ background-color: white;
}
.download-sqlite em {
diff --git a/datasette/templates/table.html b/datasette/templates/table.html
index 077332dc01..211352b507 100644
--- a/datasette/templates/table.html
+++ b/datasette/templates/table.html
@@ -162,7 +162,9 @@
{{ extra_wheres_for_ui|length }} extra where clause{% if extra_wheres_for_ui
{% endif %}
{% endfor %}
{% if facet_info.truncated %}
-
...
+ {% if request.args._facet_size != "max" -%}
+ …{% else -%}…{% endif %}
+
{% endif %}
diff --git a/tests/fixtures.py b/tests/fixtures.py
index 0a721d3a15..5730c1bf7f 100644
--- a/tests/fixtures.py
+++ b/tests/fixtures.py
@@ -129,16 +129,16 @@ def make_app_client(
files.append(extra_filepath)
os.chdir(os.path.dirname(filepath))
config = config or {}
- config.update(
- {
- "default_page_size": 50,
- "max_returned_rows": max_returned_rows or 100,
- "sql_time_limit_ms": sql_time_limit_ms or 200,
- # Default is 3 but this results in "too many open files"
- # errors when running the full test suite:
- "num_sql_threads": 1,
- }
- )
+ for key, value in {
+ "default_page_size": 50,
+ "max_returned_rows": max_returned_rows or 100,
+ "sql_time_limit_ms": sql_time_limit_ms or 200,
+ # Default is 3 but this results in "too many open files"
+ # errors when running the full test suite:
+ "num_sql_threads": 1,
+ }.items():
+ if key not in config:
+ config[key] = value
ds = Datasette(
files,
immutables=immutables,
diff --git a/tests/test_html.py b/tests/test_html.py
index 9e86ebc213..4f2cc8ad7e 100644
--- a/tests/test_html.py
+++ b/tests/test_html.py
@@ -1612,3 +1612,65 @@ def test_navigation_menu_links(
assert (
details.find("a", {"href": link}) is None
), f"{link} found but should not have been in nav menu"
+
+
+@pytest.mark.parametrize(
+ "max_returned_rows,path,expected_num_facets,expected_ellipses,expected_ellipses_url",
+ (
+ (
+ 5,
+ # Default should show 2 facets
+ "/fixtures/facetable?_facet=neighborhood",
+ 2,
+ True,
+ "/fixtures/facetable?_facet=neighborhood&_facet_size=max",
+ ),
+ # _facet_size above max_returned_rows should show max_returned_rows (5)
+ (
+ 5,
+ "/fixtures/facetable?_facet=neighborhood&_facet_size=50",
+ 5,
+ True,
+ "/fixtures/facetable?_facet=neighborhood&_facet_size=max",
+ ),
+ # If max_returned_rows is high enough, should return all
+ (
+ 20,
+ "/fixtures/facetable?_facet=neighborhood&_facet_size=max",
+ 14,
+ False,
+ None,
+ ),
+ # If num facets > max_returned_rows, show ... without a link
+ # _facet_size above max_returned_rows should show max_returned_rows (5)
+ (
+ 5,
+ "/fixtures/facetable?_facet=neighborhood&_facet_size=max",
+ 5,
+ True,
+ None,
+ ),
+ ),
+)
+def test_facet_more_links(
+ max_returned_rows,
+ path,
+ expected_num_facets,
+ expected_ellipses,
+ expected_ellipses_url,
+):
+ with make_app_client(
+ config={"max_returned_rows": max_returned_rows, "default_facet_size": 2}
+ ) as client:
+ response = client.get(path)
+ soup = Soup(response.body, "html.parser")
+ lis = soup.select("#facet-neighborhood ul li:not(.facet-truncated)")
+ facet_truncated = soup.select_one(".facet-truncated")
+ assert len(lis) == expected_num_facets
+ if not expected_ellipses:
+ assert facet_truncated is None
+ else:
+ if expected_ellipses_url:
+ assert facet_truncated.find("a")["href"] == expected_ellipses_url
+ else:
+ assert facet_truncated.find("a") is None