Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Autocomplete text entry for filter values that correspond to facets #1890

Closed
fgregg opened this issue Nov 14, 2022 · 17 comments
Closed

Autocomplete text entry for filter values that correspond to facets #1890

fgregg opened this issue Nov 14, 2022 · 17 comments

Comments

@fgregg
Copy link
Contributor

fgregg commented Nov 14, 2022

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.

@simonw
Copy link
Owner

simonw commented Nov 15, 2022

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

@simonw
Copy link
Owner

simonw commented Nov 15, 2022

I tried this out on https://congress-legislators.datasettes.com/legislators/legislator_terms for the party column - here's the demo:

datalist

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.

@simonw
Copy link
Owner

simonw commented Nov 15, 2022

This could start out as a purely JavaScript enhancement for pages that already figured out the available values through faceting, like you suggested.

@simonw
Copy link
Owner

simonw commented Nov 15, 2022

This finds the right links on the page:

document.querySelectorAll('.facet-results [data-column] li:not(.facet-truncated) a')

@simonw
Copy link
Owner

simonw commented Nov 15, 2022

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);
  }
});

@simonw
Copy link
Owner

simonw commented Nov 15, 2022

That prototype actually works really well! I'm going to add that to table.js.

@simonw simonw changed the title [feature request] drop down for facetted, named parameters Autocomplete text entry for filter values that correspond to facets Nov 15, 2022
simonw added a commit that referenced this issue Nov 15, 2022
@simonw
Copy link
Owner

simonw commented Nov 15, 2022

Wrote a TIL about <datalist>: https://til.simonwillison.net/html/datalist

@simonw
Copy link
Owner

simonw commented Nov 15, 2022

Demo now live here: https://congress-legislators.datasettes.com/legislators/legislator_terms?_facet=party - select party and start typing.

@simonw
Copy link
Owner

simonw commented Nov 15, 2022

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.

@simonw
Copy link
Owner

simonw commented Nov 15, 2022

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>

@simonw
Copy link
Owner

simonw commented Nov 15, 2022

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).

@simonw
Copy link
Owner

simonw commented Nov 16, 2022

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&amp;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&amp;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:

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&amp;foreign_key_with_blank_label=3">'
"-</a> 1</li>"
) in response.text

simonw added a commit that referenced this issue Nov 16, 2022
@simonw
Copy link
Owner

simonw commented Nov 16, 2022

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.

@simonw
Copy link
Owner

simonw commented Nov 16, 2022

@simonw
Copy link
Owner

simonw commented Nov 16, 2022

Actually this works as it should in desktop Safari:

autocomplete-safari

I'm going to just put up with the weird behaviour in Mobile Safari.

@simonw simonw closed this as completed Nov 16, 2022
@fgregg
Copy link
Contributor Author

fgregg commented Nov 17, 2022

amazing! thanks @simonw

@tddschn
Copy link

tddschn commented Feb 13, 2024

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants