Closed fgregg closed 2 years ago
Oh interesting... this doesn't even need to be attached to the visible faceting feature, necessarily: Datasette could try to detect when a column has a limited number of options (which the faceting code handles already) and could turn those into an auto-complete interface.
There's actually a native HTML element for this these days: the <datalist>
https://developer.mozilla.org/en-US/docs/Web/HTML/Element/datalist
I tried this out on https://congress-legislators.datasettes.com/legislators/legislator_terms for the party
column - here's the demo:
I made this work by dropping the following HTML into the page in the browser DevTools:
<datalist id="party">
<option value="Anti-Administration">
<option value="Pro-Administration">
<option value="Republican">
<option value="Federalist">
<option value="Democratic Republican">
<option value="Pro-administration">
<option value="Anti-administration">
<option value="Unknown">
<option value="Adams">
<option value="Jackson">
<option value="Jackson Republican">
<option value="Crawford Republican">
<option value="Whig">
<option value="Jacksonian Republican">
<option value="Jacksonian">
<option value="Anti-Jacksonian">
<option value="Adams Democrat">
<option value="Nullifier">
<option value="Anti Mason">
<option value="Anti Masonic">
<option value="Anti Jacksonian">
<option value="Democrat">
<option value="Anti Jackson">
<option value="Union Democrat">
<option value="Conservative">
<option value="Ind. Democrat">
<option value="Independent">
<option value="Law and Order">
<option value="American">
<option value="Liberty">
<option value="Free Soil">
<option value="Ind. Republican-Democrat">
<option value="Ind. Whig">
<option value="Unionist">
<option value="States Rights">
<option value="Anti-Lecompton Democrat">
<option value="Constitutional Unionist">
<option value="Independent Democrat">
<option value="Unconditional Unionist">
<option value="Conservative Republican">
<option value="Ind. Republican">
<option value="Liberal Republican">
<option value="National Greenbacker">
<option value="Readjuster Democrat">
<option value="Readjuster">
<option value="Union">
<option value="Union Labor">
<option value="Populist">
<option value="Silver Republican">
<option value="Free Silver">
<option value="Silver">
<option value="Democratic and Union Labor">
<option value="Progressive Republican">
<option value="Progressive">
<option value="Prohibitionist">
<option value="Socialist">
<option value="Farmer-Labor">
<option value="American Labor">
<option value="Nonpartisan">
<option value="Coalitionist">
<option value="Popular Democrat">
<option value="Liberal">
<option value="New Progressive">
<option value="Republican-Conservative">
<option value="Democrat-Liberal">
<option value="AL">
<option value="Libertarian">
</datalist>
And then adding list="party"
to the input element in the filter form.
This could start out as a purely JavaScript enhancement for pages that already figured out the available values through faceting, like you suggested.
This finds the right links on the page:
document.querySelectorAll('.facet-results [data-column] li:not(.facet-truncated) a')
Here's a prototype:
function createDataLists() {
var facetResults = document.querySelectorAll(".facet-results [data-column]");
Array.from(facetResults).forEach(function (facetResult) {
// Use link text from all links in the facet result
var linkTexts = Array.from(
facetResult.querySelectorAll("li:not(.facet-truncated) a")
).map(function (link) {
return link.textContent;
});
// Create a datalist element
var datalist = document.createElement("datalist");
datalist.id = "datalist-" + facetResult.dataset.column;
// Create an option element for each link text
linkTexts.forEach(function (linkText) {
var option = document.createElement("option");
option.value = linkText;
datalist.appendChild(option);
});
// Add the datalist to the facet result
facetResult.appendChild(datalist);
});
}
createDataLists();
// When any select with name=_filter_column changes, update the datalist
document.body.addEventListener("change", function (event) {
if (event.target.name === "_filter_column") {
event.target
.closest(".filter-row")
.querySelector(".filter-value")
.setAttribute("list", "datalist-" + event.target.value);
}
});
That prototype actually works really well! I'm going to add that to table.js
.
Wrote a TIL about <datalist>
: https://til.simonwillison.net/html/datalist
Demo now live here: https://congress-legislators.datasettes.com/legislators/legislator_terms?_facet=party - select party
and start typing.
Spotted a bug with this on https://latest.datasette.io/fixtures/facetable?_facet=_city_id - the _city_id
column is a foreign key, so you need to type 1
or 2
- but the autocomplete list shows the full text names for the cities.
Looks like I can fix that like so:
<datalist id="datalist-_city_id">
<option label="San Francisco" value="1"></option>
<option label="Los Angeles" value="2"></option>
<option label="Detroit" value="3"></option>
<option label="Memnonia" value="4"></option>
</datalist>
Annoying: Mobile Safari doesn't seem to support separate labels and values. I should probably disable this feature on that browser, at least for foreign key facets (for the moment).
Oops, introduced a test failure:
def test_table_html_foreign_key_facets(app_client):
response = app_client.get(
"/fixtures/foreign_key_references?_facet=foreign_key_with_blank_label"
)
assert response.status == 200
> assert (
'<li><a href="http://localhost/fixtures/foreign_key_references?_facet=foreign_key_with_blank_label&foreign_key_with_blank_label=3">'
"-</a> 1</li>"
) in response.text
E assert '<li><a href="http://localhost/fixtures/foreign_key_references?_facet=foreign_key_with_blank_label&foreign_key_with_blank_label=3">-</a> 1</li>' in '<!DOCTYPE html>\n<html>\n<head>\n <title>fixtures: foreign_key_references: 2 rows</title>\n <link rel="styleshe.../script>\n\n\n<!-- Templates considered: table-fixtures-foreign_key_references.html, *table.html -->\n</body>\n</html>'
E + where '<!DOCTYPE html>\n<html>\n<head>\n <title>fixtures: foreign_key_references: 2 rows</title>\n <link rel="styleshe.../script>\n\n\n<!-- Templates considered: table-fixtures-foreign_key_references.html, *table.html -->\n</body>\n</html>' = <datasette.utils.testing.TestResponse object at 0x7fd1b0080640>.text
Need to fix this test:
Here's a polyfill for <datalist>
: https://github.com/mfranzke/datalist-polyfill
It shouldn't be necessary now that Safari has shipped support (apparently added in https://developer.apple.com/documentation/safari-release-notes/safari-12_1-release-notes#3130314 Safari 12.1 in March 2019).
But it does look like Safari doesn't support differing label
and value
attributes, though documentation about this is hard to come by.
https://bugs.webkit.org/show_bug.cgi?id=201768 - " Datalist option's label not used" - marked as RESOLVED FIXED on March 31st 2020.
The commit: https://trac.webkit.org/changeset/259330/webkit
And here's the test mirrored on GitHub: https://cs.github.com/qtwebkit/webkit-mirror/blob/cc3fcd0b4bad1f7cf77c26e34aa01d16618d6d5e/LayoutTests/fast/forms/datalist/datalist-option-labels.html?q=datalist-option-labels.html
Actually this works as it should in desktop Safari:
I'm going to just put up with the weird behaviour in Mobile Safari.
amazing! thanks @simonw
Demo now live here: https://congress-legislators.datasettes.com/legislators/legislator_terms?_facet=party - select
party
and start typing.
The demo works, but it doesn't seem to work for me on this one: https://congress-legislators.datasettes.com/legislators/legislator_terms?_facet=type&_facet=party&_facet=state&type=sen&party=Republican&_facet_size=max&state=NC&_sort_desc=start
And when I run datasette 0.64.6
locally, the autocompletion doesn't seem to work either.
Browser: Google Chrome Canary 123.0.6298.0 on macOS 14.3
datasette allows users to enter in the value for named parameters into a free-text form field.
I think it would add a lot of usability, if the form field could be a drop down of options when query value is already a faceted column.