import { Meta, Story, Source, Primary, Controls } from "@storybook/addon-docs"; import * as stories from "./table.stories"; # Table The table component provides a comprehensive way to display, sort and filter data. It consists of two portions, a UI component called `bit-table` and the underlying data source `TableDataSource`. This documentation will initially focus on the UI portion before covering the data source. ## UI Component The UI component consists of a couple of elements. - `bit-table`: The main component that creates a native table element and applies the table styling. - `header`: A container for the table header. - `body`: A container for the table body. - `bitCell`: A cell within the table. Used for both headers and content. ### Guidelines - Always include a row or column header with your table; this allows screen readers to better contextualize the data. - Avoid spanning data across cells. - When a cell contains an interactive element, the whole cell should be selectable and trigger the action not just the element the cell contains. - Be sure to make repeating actions unique by associating them with the object they relate to. Example: if there are multiple “Edit” buttons on a table, a screen reader should read “Edit, Netflix” for an edit option for a Netflix item. - Use [Virtual Scrolling](#virtual-scrolling) for large data sets. - For bulk menu options, display a 3 dot menu in the header. When multiple items are selected, the bulk menu will contain actions that can be completed in bulk for the selected items. - Note, this may result in some menu actions being hidden at times if they are not applicable to the selected item - Clicking on a row’s 3 dot menu will continue to result in actions specific to just that row's item ### Usage The below code is the minimum required to create a table. However we strongly advise you to use the `dataSource` input to provide a data source for your table. This allows you to easily sort data. ```html Header 1 Header 2 Header 3 Cell 1 Cell 2 Cell 3 ``` ## Data Source Bitwarden provides a data source for tables that can be used in place of a traditional data array. The `TableDataSource` implements sorting and filtering capabilities. This allows the `bitTable` component to focus on rendering while offloading the data management to the data source. ```ts // External data source const data: T[]; const dataSource = new TableDataSource(); dataSource.data = data; ``` We use the `dataSource` as an input to the `bit-table` component, and access the rows to render within the `ng-template`which provides access to the rows using `let-rows$`. ```html Id Name Other {{ r.id }} {{ r.name }} {{ r.other }} ``` ### Sorting We provide a simple component for displaying sortable column headers. The `bitSortable` component wires up to the `TableDataSource` and will automatically sort the data when clicked and display an indicator for which column is currently sorted. The default sorting can be specified by setting the `default`. ```html Id Name ``` For default sorting in descending order, set default="desc" ```html Name ``` It's also possible to define a custom sorting function by setting the `fn` input. ```ts // Basic sort function const sortFn = (a: T, b: T) => (a.id > b.id ? 1 : -1); // Direction aware sort function const sortByName = (a: T, b: T, direction?: SortDirection) => { const result = a.name.localeCompare(b.name); return direction === "asc" ? result : -result; }; ``` ### Filtering Filtering is supported by passing a filter predicate to `filter`. ```ts dataSource.filter = (data) => data.orgType === "family"; ``` Rudimentary string filtering is supported out of the box with `TableDataSource.simpleStringFilter`. It works by converting each entry into a string of it's properties. The provided string is then compared against the filter value using a simple `indexOf` check. For convienence, you can also just pass a string directly. ```ts dataSource.filter = TableDataSource.simpleStringFilter("search value"); // or dataSource.filter = "search value"; ``` ### Virtual Scrolling It's heavily adviced to use virtual scrolling if you expect the table to have any significant amount of data. This is done by using the `bit-table-scroll` component instead of the `bit-table` component. This component behaves slightly different from the `bit-table` component. Instead of using the `*ngFor` directive to render the rows, you provide a `bitRowDef` template that will be used for rendering the rows. Due to limitations in the Angular Component Dev Kit you must provide an `rowSize` which corresponds to the height of each row. If the height of the rows are not uniform, you should set an explicit row height and align vertically. ```html Id Name Other {{ row.id }} {{ row.name }} {{ row.other }} ``` ## Accessibility - Always include a row or column header with your table; this allows assistive technology to better contextualize the data - Avoid spanning data across cells - Be sure to make repeating actions unique by associating them with the object they relate to - **Example:** if there are multiple “Edit” buttons on a table, a screen reader should read “Edit, Netflix” for an edit option for a Netflix item.