{
  "id": "FI01",
  "title": "Filter drawer",
  "topic": "filter",
  "use": "The drawer is where filters go by default. A click on the filter button and it slides in from the right. It has room for any number of filters, groups among them. Every change takes effect at once — there is no Apply. Modal over the list, contained in the content region, or contained and pushing the list aside — each of the three is right for filters; pick the one the screen needs.",
  "decisions": [
    "The drawer is the default for filters: it has room for any number of filters and for groups, which a panel in the toolbar does not.",
    "It opens from the trailing edge: navigation comes from the leading edge, and supplementary surfaces come from the other side.",
    "Modal, contained, and contained with push are each valid for filters, and the screen picks the one it needs. Modal lays the filter surface over the whole height and makes filtering a step of its own, at the price that the list behind it cannot be used while it is open.",
    "The filter button says what it does — 'Filter' when closed, 'Close filter' when open. Where the drawer leaves the page usable, contained with push, a second click closes it; a modal or contained drawer covers the button and closes by Done, the cross or Escape.",
    "There is no Apply: every change takes effect at once, so closing discards nothing.",
    "The footer carries the way out, not an Apply: Done closes and Reset all filters clears. Closing needs a named button because the result is seen only once the drawer is shut, and the reset is in the footer so nothing has to be closed first to clear everything.",
    "The count stays in the row under the toolbar even with no filter, as the bare number, and 'X of Y' when filtered: it is the only statement of how much is not shown. Each filtered column adds a badge with its dimension and values, followed by the reset, which goes away with the last badge.",
    "The empty state names the filter as the cause and offers the reset in the same place, so 'no results' is not read as 'no data'."
  ],
  "try": [
    {
      "do": "Press the filter button",
      "then": "the drawer slides in from the right and lies over the list, with the backdrop behind it"
    },
    {
      "do": "Click beside the drawer",
      "then": "it closes, as a modal drawer does"
    },
    {
      "do": "Set a filter in the drawer",
      "then": "the count under the toolbar changes with the click"
    },
    {
      "do": "Switch Drawer to Contained and push, open the drawer and press the button again",
      "then": "it now reads “Close filter” and does that; the cross closes it as well, and so does Escape under Modal and Contained"
    },
    {
      "do": "Look for Apply in the footer",
      "then": "there is none: Done and the reset stand there"
    },
    {
      "do": "Press Done in the footer",
      "then": "the drawer closes, and the button in the toolbar reads Filter again"
    },
    {
      "do": "Reset in the footer while the drawer is open",
      "then": "every field empties and the list is complete again, without closing the drawer"
    }
  ],
  "revision": 1,
  "changes": [],
  "reviewed": true,
  "source": "<!-- Demo only, not part of the pattern. A switch between the three drawers the pattern allows. A product picks one: modal as written here, contained with the drawer's contained attribute, or contained and push with both contained and push. -->\n\n<div class=\"fi01-region\">\n  <ed-data-grid id=\"fi01-documents\" label=\"Documents\" with-search>\n    <ed-button slot=\"toolbar-actions\">\n      <ed-icon slot=\"start\" name=\"plus\"></ed-icon>\n      Create document\n    </ed-button>\n    <ed-button id=\"fi01-filter\" slot=\"toolbar-view\" appearance=\"plain\">\n      <ed-icon slot=\"start\" name=\"filter\"></ed-icon>\n      <span id=\"fi01-filter-label\">Filter</span>\n    </ed-button>\n    <ed-empty-state slot=\"no-results\" icon=\"search\" size=\"medium\">\n      <span slot=\"label\">No document matches the active filters</span>\n      <span id=\"fi01-reason\"></span>\n      <ed-button slot=\"button\" appearance=\"outlined\" data-reset>\n        <ed-icon slot=\"start\" name=\"close\"></ed-icon>\n        Reset all filters\n      </ed-button>\n    </ed-empty-state>\n  </ed-data-grid>\n\n  <ed-drawer id=\"fi01-drawer\" label=\"Filter\" placement=\"end\" light-dismiss with-footer>\n    <div class=\"ed-stack\">\n      <ed-select data-column=\"type\" label=\"Document type\" placeholder=\"All\" multiple with-clear>\n        <ed-option value=\"Bank voucher\">Bank voucher</ed-option>\n        <ed-option value=\"Cash receipt\">Cash receipt</ed-option>\n        <ed-option value=\"Incoming invoice\">Incoming invoice</ed-option>\n        <ed-option value=\"Journal entry\">Journal entry</ed-option>\n        <ed-option value=\"Outgoing invoice\">Outgoing invoice</ed-option>\n      </ed-select>\n      <ed-select data-column=\"clerk\" label=\"Processed by\" placeholder=\"All\" multiple with-clear>\n        <ed-option value=\"Andrea Jansen\">Andrea Jansen</ed-option>\n        <ed-option value=\"Marie-Luise Hoffmann-Grote\">Marie-Luise Hoffmann-Grote</ed-option>\n        <ed-option value=\"Sophie Brandt\">Sophie Brandt</ed-option>\n        <ed-option value=\"Thorsten Reinke\">Thorsten Reinke</ed-option>\n      </ed-select>\n      <ed-select data-column=\"status\" label=\"Status\" placeholder=\"All\" multiple with-clear>\n        <ed-option value=\"Error\">Error</ed-option>\n        <ed-option value=\"Finalized\">Finalized</ed-option>\n        <ed-option value=\"Open\">Open</ed-option>\n        <ed-option value=\"Reviewed\">Reviewed</ed-option>\n      </ed-select>\n    </div>\n    <ed-button slot=\"footer\" data-drawer=\"close\">Done</ed-button>\n    <ed-button slot=\"footer\" appearance=\"plain\" data-reset>Reset all filters</ed-button>\n  </ed-drawer>\n</div>\n\n<!-- Demo only, not part of the pattern. The content region takes the height the screen leaves it; here it is set to fit the frame. -->\n\n<style>\n  .fi01-region {\n    display: flex;\n    position: relative;\n    overflow: hidden;\n  }\n\n  .fi01-region > ed-data-grid {\n    flex: 1;\n    min-inline-size: 0;\n  }\n</style>\n\n<!-- Demo only, not part of the pattern. The product supplies documents: the documents of the list, as { id, text, type, clerk, status }. -->\n\n<script type=\"module\">\n  await customElements.whenDefined('ed-data-grid');\n\n  const grid = document.getElementById('fi01-documents');\n  const drawer = document.getElementById('fi01-drawer');\n  const reason = document.getElementById('fi01-reason');\n  const fields = [...drawer.querySelectorAll('ed-select')];\n\n  const statusVariant = { Open: 'neutral', Reviewed: 'info', Finalized: 'success', Error: 'danger' };\n  const labels = { type: 'Document type', clerk: 'Processed by', status: 'Status' };\n\n  grid.columns = [\n    { field: 'id', label: 'Document', width: 152, rowheader: true, pinned: 'left' },\n    { field: 'text', label: 'Posting text', minWidth: 224 },\n    { field: 'type', label: labels.type, width: 176, filterable: true, filterType: 'set' },\n    { field: 'clerk', label: labels.clerk, width: 208, filterable: true, filterType: 'set' },\n    {\n      field: 'status',\n      label: labels.status,\n      width: 144,\n      pinned: 'right',\n      filterable: true,\n      filterType: 'set',\n      formatter: value => {\n        const badge = document.createElement('ed-badge');\n        badge.setAttribute('variant', statusVariant[value]);\n        badge.textContent = value;\n        return badge;\n      }\n    }\n  ];\n  grid.data = documents;\n\n  // The empty state names what narrows the list, so it is not read as \"there is no data\".\n  function describe() {\n    const active = grid.filters.map(filter => `${labels[filter.id]} — ${filter.value.join(', ')}`);\n    if (grid.searchTerm.trim()) active.push(`Search — ${grid.searchTerm.trim()}`);\n    reason.textContent = `Active: ${active.join(' · ')}`;\n  }\n\n  function showFilters() {\n    for (const field of fields) field.value = grid.filters.find(filter => filter.id === field.dataset.column)?.value ?? [];\n    describe();\n  }\n\n  for (const field of fields) {\n    field.addEventListener('change', () => {\n      const others = grid.filters.filter(filter => filter.id !== field.dataset.column);\n      grid.filters = field.value?.length ? [...others, { id: field.dataset.column, value: field.value }] : others;\n      describe();\n    });\n  }\n\n  grid.addEventListener('ed-data-grid-filter-change', showFilters);\n\n  document.getElementById('fi01-filter').addEventListener('click', () => {\n    drawer.open = !drawer.open;\n  });\n\n  // The label carries the state: ed-button does not pass aria-expanded on to its inner button. It is\n  // read from `open`, because a close and a quick reopen deliver the close's ed-after-hide after the\n  // reopen's ed-show.\n  const filterLabel = document.getElementById('fi01-filter-label');\n  const showLabel = () => (filterLabel.textContent = drawer.open ? 'Close filter' : 'Filter');\n  drawer.addEventListener('ed-show', showLabel);\n  drawer.addEventListener('ed-after-hide', showLabel);\n\n  for (const reset of document.querySelectorAll('[data-reset]')) {\n    reset.addEventListener('click', () => {\n      grid.filters = [];\n      grid.searchTerm = '';\n      showFilters();\n    });\n  }\n\n  grid.filters = [{ id: 'status', value: ['Open'] }];\n  showFilters();\n</script>",
  "page": "https://test-apps.eurodata.de/a/eurodata-ui/docs/patterns/filter/fi01-filter-drawer/",
  "topicPage": "https://test-apps.eurodata.de/a/eurodata-ui/docs/patterns/filter/"
}
