Filtering
Table filtering principles
Standard table filtering
Shipment | Recipient | Sent | Recieved | Status |
---|---|---|---|---|
987654321 | Mieow Corp | 09.08.2024 11.50 | 12.08.2024 11.38 | Delivered |
123456789 | Shiba Shipping Industries | 10.08.2024 12.12 | 14.08.2024 11.05 | Delivered |
456789123 | Toad Trampolines Co. | 02.09.2024 10.38 | In transit |
HTML
<div class="flex gas align-ic">
<label class="form__label" for="search-input">Shipment or recipient
<input type="text" id="search-input" class="form__control w16r" placeholder="Search" aria-controls="result-content" />
</label>
<label class="form__label" for="status-select">Status
<select id="status-select" class="form__control wauto maxw100p" aria-controls="result-content">
<option>All statuses</option>
<option selected data-filter="status">Delivered</option>
<option data-filter="status">In transit</option>
</select>
</label>
<button id="clear-filters" class="btn-link--dark mts mls">Clear all</span></button>
</div>
<div id="result-content">
<div id="result-message" role="status" class="screen-reader-text mlm" aria-live="polite"></div>
<table id="example-table" class="mb-table mts">
<thead>
<tr>
<th scope="col">Shipment</th>
<th scope="col">Recipient</th>
<th scope="col">Sent</th>
<th scope="col">Recieved</th>
<th scope="col">Status</th>
</tr>
</thead>
<tbody>
<tr>
<td data-filter="shipment"><a href="#">987654321</a></td>
<td data-filter="recipient">Mieow Corp</td>
<td>09.08.2024 11.50</td>
<td>12.08.2024 11.38</td>
<td data-filter="status"><span class="mb-badge mb-badge--green">Delivered</span></td>
</tr>
<tr>
<td data-filter="shipment"><a href="#">123456789</a></td>
<td data-filter="recipient">Shiba Shipping Industries</td>
<td>10.08.2024 12.12</td>
<td>14.08.2024 11.05</td>
<td data-filter="status"><span class="mb-badge mb-badge--green">Delivered</span></td>
</tr>
<tr hidden>
<td data-filter="shipment"><a href="#">456789123</a></td>
<td data-filter="recipient">Toad Trampolines Co.</td>
<td>02.09.2024 10.38</td>
<td></td>
<td data-filter="status"><span class="mb-badge">In transit</span></td>
</tr>
</tbody>
</table>
</div>
<div id="rowselect-pagination" class="flex flex-wrap align-ic gas">
<div class="mb-rows-select">
<select id="rows-select" class="form__control wauto maxw100p">
<option>25</option>
<option>50</option>
<option>100</option>
</select>
<label for="rows-select">Shipments per page</label>
</div>
<nav aria-label="Pagination">
<ul class="pagination">
<li>
<button title="Previous page">
<span
data-mybicon="mybicon-arrow-left"
data-mybicon-class="icon-ui"
></span>
</button>
</li>
<li>
<button>1</button>
</li>
<li>
<button aria-current="page">2</button>
</li>
<li>
<button>3</button>
</li>
<li>
<button>4</button>
</li>
<li>
<button title="Next page">
<span
data-mybicon="mybicon-arrow-right"
data-mybicon-class="icon-ui"
></span>
</button>
</li>
</ul>
</nav>
</div>
Extended table filtering
When a table has a lot of filters, or not all filters are used often.
Shipment | Recipient | Sent | Recieved | Contact Person | Country | Status |
---|---|---|---|---|---|---|
987654321 | Mieow Corp | 09.08.2024 11.50 | 12.08.2024 11.38 | Tom | Sweden | Delivered |
123456701 | Shiba Shipping Industries | 10.08.2024 12.12 | 14.08.2024 11.05 | Mr. Woof | Norway | Delivered |
123456702 | Shiba Shipping Industries | 10.08.2024 12.30 | 14.08.2024 11.05 | Mr. Woof | Norway | Delivered |
123456703 | Shiba Shipping Industries | 13.09.2024 17.02 | 15.09.2024 11.21 | Mr. Woof | Norway | Delivered |
123456704 | Shiba Shipping Industries | 07.10.2024 10.44 | 10.10.2024 12.00 | Mr. Woof | Norway | Delivered |
123456705 | Shiba Shipping Industries | 11.10.2024 14.34 | 14.10.2024 11.22 | Mr. Woof | Norway | Delivered |
456789123 | Toad Trampolines Co. | 02.09.2024 10.38 | Skippy | Norway | In transit |
HTML
<div class="flex align-ic gam justify-csb flex-wrap mbm">
<label class="form__label mb0" for="search-input-extended">
Shipment or recipient
<input type="text" id="search-input-extended" aria-controls="result-content" class="form__control w16r mb0" placeholder="Search"/>
</label>
<div class="flex flex-wrap gas align-sfe">
<button id="extended-search-btn" aria-controls="extended-search" aria-expanded="false" class="btn mb0 relative">
<span id="rotate" data-mybicon="mybicon-arrow-right" data-mybicon-class="icon-ui mrxs rotate"></span>
Extended filtering
<span id="active-extended" class="mb-dot"></span>
</button>
<button class="btn mb0">
<span data-mybicon="mybicon-download_file" data-mybicon-class="icon-ui mrxs"></span>
Export
</button>
<button title="Column customization" aria-controls="column-customization" class="btn mb0" id="column-custom-btn">
<span data-mybicon="mybicon-columns-edit" data-mybicon-class="icon-ui mlxs"></span>
</button>
</div>
</div>
<div class="flex flex-wrap bg-gray2 mbm pam pbs gas dn" id="extended-search" aria-expanded="false">
<label class="form__label" for="status-select-extended">Status
<select id="status-select-extended" class="form__control wauto maxw100p" aria-controls="result-content">
<option>All statuses</option>
<option selected data-filter="status">Delivered</option>
<option data-filter="status">In transit</option>
</select>
</label>
<label class="form__label" for="country-select">
Country
<select id="country-select" class="form__control wauto maxw100p" aria-controls="result-content">
<option selected>All countries</option>
<option data-filter="country">Norway</option>
<option data-filter="country">Sweden</option>
<option data-filter="country">Denmark</option>
</select>
</label>
<label class="form__label" for="contact-select">
Contact person
<select id="contact-select" class="form__control wauto maxw100p" aria-controls="result-content">
<option selected>All contacts</option>
<option data-filter="contact">Tom</option>
<option data-filter="contact">Mr. Woof</option>
<option data-filter="contact">Skippy</option>
</select>
</label>
</div>
<div class="flex flex-wrap gas">
<button id="clear-filters-extended" class="btn-link--dark mrs">
Clear all
</button>
<button id="input-chip" class="btn-link--dark dn" title="Search value"><span id="input-chip-val"></span><span
data-mybicon="mybicon-cross" data-mybicon-class="icon-ui mls"></span></button>
<button id="select-chip" class="btn-link--dark" title="Selected status"><span
id="select-chip-val">Delivered</span><span data-mybicon="mybicon-cross"
data-mybicon-class="icon-ui mls"></span></button>
<button id="country-chip" class="btn-link--dark dn" title="Selected country"><span id="country-chip-val"></span><span
data-mybicon="mybicon-cross" data-mybicon-class="icon-ui mls"></span></button>
<button id="contact-chip" class="btn-link--dark dn" title="Selected contact person"><span
id="contact-chip-val"></span><span data-mybicon="mybicon-cross" data-mybicon-class="icon-ui mls"></span></button>
</div>
<div id="result-content">
<div id="result-message-extended" role="status" class="screen-reader-text mlm mtl" aria-live="polite"></div>
<table id="example-table-extended" class="mb-table mts">
<thead>
<tr>
<th scope="col">Shipment</th>
<th scope="col">Recipient</th>
<th scope="col">Sent</th>
<th scope="col">Recieved</th>
<th scope="col">Contact Person</th>
<th scope="col">Country</th>
<th scope="col">Status</th>
</tr>
</thead>
<tbody>
<tr>
<td data-filter="shipment"><a href="#">987654321</a></td>
<td data-filter="recipient">Mieow Corp</td>
<td>09.08.2024 11.50</td>
<td>12.08.2024 11.38</td>
<td>Tom</td>
<td data-filter="country">Sweden</td>
<td data-filter="status"><span class="mb-badge mb-badge--green">Delivered</span></td>
</tr>
<tr>
<td data-filter="shipment"><a href="#">123456701</a></td>
<td data-filter="recipient">Shiba Shipping Industries</td>
<td>10.08.2024 12.12</td>
<td>14.08.2024 11.05</td>
<td>Mr. Woof</td>
<td data-filter="country">Norway</td>
<td data-filter="status"><span class="mb-badge mb-badge--green">Delivered</span></td>
</tr>
<tr>
<td data-filter="shipment"><a href="#">123456702</a></td>
<td data-filter="recipient">Shiba Shipping Industries</td>
<td>10.08.2024 12.30</td>
<td>14.08.2024 11.05</td>
<td>Mr. Woof</td>
<td data-filter="country">Norway</td>
<td data-filter="status"><span class="mb-badge mb-badge--green">Delivered</span></td>
</tr>
<tr>
<td data-filter="shipment"><a href="#">123456703</a></td>
<td data-filter="recipient">Shiba Shipping Industries</td>
<td>13.09.2024 17.02</td>
<td>15.09.2024 11.21</td>
<td>Mr. Woof</td>
<td data-filter="country">Norway</td>
<td data-filter="status"><span class="mb-badge mb-badge--green">Delivered</span></td>
</tr>
<tr>
<td data-filter="shipment"><a href="#">123456704</a></td>
<td data-filter="recipient">Shiba Shipping Industries</td>
<td>07.10.2024 10.44</td>
<td>10.10.2024 12.00</td>
<td>Mr. Woof</td>
<td data-filter="country">Norway</td>
<td data-filter="status"><span class="mb-badge mb-badge--green">Delivered</span></td>
</tr>
<tr>
<td data-filter="shipment"><a href="#">123456705</a></td>
<td data-filter="recipient">Shiba Shipping Industries</td>
<td>11.10.2024 14.34</td>
<td>14.10.2024 11.22</td>
<td>Mr. Woof</td>
<td data-filter="country">Norway</td>
<td data-filter="status"><span class="mb-badge mb-badge--green">Delivered</span></td>
</tr>
<tr>
<td data-filter="shipment"><a href="#">456789123</a></td>
<td data-filter="recipient">Toad Trampolines Co.</td>
<td>02.09.2024 10.38</td>
<td></td>
<td>Skippy</td>
<td data-filter="country">Norway</td>
<td data-filter="status"><span class="mb-badge">In transit</span></td>
</tr>
</tbody>
</table>
</div>
<div id="rowselect-pagination-extended" class="flex flex-wrap align-ic gas">
<div class="mb-rows-select">
<select id="rows-select-extended" class="form__control wauto maxw100p">
<option>25</option>
<option>50</option>
<option>100</option>
</select>
<label for="rows-select-extended">Shipments per page</label>
</div>
<nav aria-label="Pagination">
<ul class="pagination">
<li>
<button title="Previous page">
<span data-mybicon="mybicon-arrow-left" data-mybicon-class="icon-ui"></span>
</button>
</li>
<li>
<button>1</button>
</li>
<li>
<button aria-current="page">2</button>
</li>
<li>
<button>3</button>
</li>
<li>
<button>4</button>
</li>
<li>
<button title="Next page">
<span data-mybicon="mybicon-arrow-right" data-mybicon-class="icon-ui"></span>
</button>
</li>
</ul>
</nav>
</div>
Principles
What to filter
Order filter by usage and importance. Track usage of filters and actions to get valuable insights into user interactions with the tables.
- Limit the amount of filters and inputs by merging text search fields, removing unused filters and placing rarely used filters into an extended filters area.
- Evaluate if a filter fills the same function as a column's sorting, and consider using only one of them.
Reference
Positions
- Place search and filter above table, with search to the left.
- If the table has extended filter, action buttons such as export and column customization buttons place above the table, aligned to the right, in the order according to usage as shown in example.
- Pagination and rowselect controls are only placed below the table, with row selection on the left. If table has many rows by default, use scroll on table.
Rows per page
Use the rows per page select for large tables with a lot of data. A good starting point for values in the select is 25 - 50 - 100 rows per page. You can add logging to see which values are used the most, and adjust the options and default value.
Wording
"Rows per page" is a good default, but it's even better to be context specific. For instance "Orders per page" or "Shipments per page".
Reference
Active filters
- Active filter is shown in form controls.
- Include "Clear all" button when a filter or search is active.
- If the table has extended filters use chips and clear all to show active filters. Chips are not necessary for simple tables with few search and filter options. Use notification dot on button if filter from extended filters is active. If filter is preselected use the wording "reset filter" instead of "clear all".
Reference
-
mb-dot
No results
Hide the entire table and pagination when search or filtering yields no results. Inform the user by displaying a message such as "No results found" instead. We also need to inform users using assistive technology such as screen readers about the change without changing focus. To do this we can
-
Populate an empty ARIA live region with content when there are no results, using
aria-live="polite"
androle="status"
. - Put
aria-controls
on elements that will affect the live region, such as search and filter inputs, using the ARIA live region id as value. - When searching again after getting "no results" message, let the user know they are getting results by announcing "results found" or "x results found". If search results are dynamic, try to avoid interrupting the user too much by not announcing results found unecessarily often, and introduce a delay to avoid interrupting the user while they are typing.
Reference
Pagination
Reset the pagination with each filtering action, as the current page may no longer be accurate. Pagination is not often being used in combination with filtering, and there is no guarantee that the current page will still exist.