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

[RFC]: DataGrid Component #198

Open
felipecadavid opened this issue Feb 21, 2024 · 0 comments
Open

[RFC]: DataGrid Component #198

felipecadavid opened this issue Feb 21, 2024 · 0 comments

Comments

@felipecadavid
Copy link
Contributor

felipecadavid commented Feb 21, 2024

What's the problem?

We already have the useDataGrid query hook working on the ui-kit, however, we don’t have a component that consumes it, also in order to implement the new SQL Playground in Console, we’ll require to have a DataGrid component for the result visualization.

What are the requirements?

Following the same pattern we have for other components, the DataGrid component should provide a higher-level API that enables customers to setup a basic DataGrid easily but also enable exposing a lower-level API to access the inner table components directly, hopefully the library we use enables us to export its API directly similar to what we do on Chart.js with chartConfigProps.

We already have the useDataGrid hook to query the data so the most important concern is how we display it and enable customizing it.

For v1, we can probably focus on getting a basic version out while we provide data visualization features that we also require on Console

  • Default state should render a table with basic styles, with a column to index rows (example from Snowflake)

image (4)

  • Columns should be resizable in order to enable viewing larger elements
  • Cells should be selectable and open a drawer with the cell’s data, in case of containing a JSON, it should be formatted. This drawer should also be customizable and provide an option to disable it
  • Rows should enable/disable showing a cell with the row’s index to the left
  • Clicking on a row’s index should select the row and open a drawer with the row’s data. This drawer should also be customizable and provide an option to disable it
  • DataGrid should include built-in pagination
  • Clicking on a column header should change sorting, clicking once should set to ASC, clicking twice should set to DESC and clicking a third time should reset, sorting should be made client-side for static mode and server-side for connected mode.

Requirements for table component:

The DataGrid component should return a table with the received data from DataGrid API,

  • Should enable column resize
  • Rows should listen to click events and send row data
  • Cells should listen to click events and send cell data
  • Should enable pagination
  • Should be based on <table /> elements for better a11y
  • Should be fully customizable
  • Should enable sorting by column
  • Should enable showing/hiding row numbers
  • Should enable showing/hiding headers
  • Should enable showing a drawer on column/cell click
  • Should support virtualized scrolling
  • Good performance with thousands of rows or more.
  • Sticky columns
  • Download query data as .csv
  • Enable keyboard interactions (selecting whole table, copying a row/cell)

In pro of following consistency with the way we implement charts, helper components, etc we should probably opt for finding a library that would best fit our needs

Candidates:

Tanstack Table (React Table)

Tanstack Table provides a headless React hook that makes it easy to implement a Table component, this is similar to what we do today with base-ui’s Select component

✅ Should enable column resize

✅ Should enable pagination

✅ Should be based on <table /> elements for better a11y (you can base it either on div elements or table)

✅ Should be fully customizable (as it’s a headless hook we have full control on the table styles’ customization)

✅ Should enable sorting by column

✅ Should enable showing/hiding row numbers

✅ Should enable showing/hiding headers

✅ Should enable showing a drawer on column/cell click

✅ Should be a lightweight library (752kb npm, 15kb with tree-shaking)

✅ Should be a well maintained library (last commit yesterday)

Pros Cons
React-Table is a popular library with 23.6k stars on https://github.com/tanstack/table Pronounced learning curve as it is not a component itself but a headless hook
Maintained by Tanstack which is the same team that maintains React-Query  
As it is a headless hook, that enables us to implement future features even if those are not directly supported by React-Table  
Extensive documentation with many examples  

react-data-grid

react-data-grid provides a react component for building data grids, react-data-grid is currently in beta

✅ Should enable column resize

❌ Should enable pagination (does not provide built-in support for pagination)

⚠️ Should be based on <table /> elements for better a11y (based on div elements)

⚠️ Should be fully customizable

  • react-data-grid exports a high level component that receives a renderers prop, this one enables overriding their Checkbox component, Row , SortStatus ,noRowsFallback, while this provides a certain level of customization some things seems to be missing (e.g header)

✅ Should enable sorting by column

✅ Should enable showing/hiding row numbers

❌ Should enable showing/hiding headers

✅ Should enable showing a drawer on column/cell click

⚠️ Should be a lightweight library (752kb might be not so lightweight for a headless hook library)

✅ Should be a well maintained library (last commit 4 days ago)

Other options are either paid or not as good as Tanstack Table, we decided to proceed with Tanstack Table

Detailed design

The component should receive a set of headers and rows for static mode, as well as other properties we already set in static mode for other components.

<DataGrid headers={[]} rows={[]} ...othercommonprops />

Subcomponents should also include a className and style prop in order to enable styles customization.

Loader state

For loader we probably want to do something different to just a loading rectangle, we probably want to try something similar to console, cells in loading state and headers shown for static mode, for connected mode cells should be in loading state as well

Untitled

Requirements implementation

Based on the decision of using Tanstack Table for the Table, we should rely on their examples on how to implement the features we require, here are some of the guides provided by Tanstack table:

Some features however we will have to implement on our own: drawer, exporting to .csv

Interactivity and customization

It’s necessary to enable handling most of interactivity via props, for this we will require:

  • Props to handle click events on Cells, Rows and Headers
  • Prop to disable/enable the drawer
  • Prop to customize drawer content

We can follow our current pattern of using fallbacks/overrides in order to provide the best level of customization, we can have RowOverride, CellOverride, DrawerOverride, HeaderOverride and PaginationOverride

Rows

For rows we would have the rowOverride but also a top-level prop in order to hide the indexes

Prop Type Description
rowOverride Element | ({props: RowProps, RowComponent: Row, CellComponent: Cell}) => Element Overrides the row component
indexColumn 'left' | 'right' | 'hide' Sets the position of the index column to either left or right or disables it.

RowProps

Prop Type Description
onIndexClick (rowData: string[] | Element[]) => void Callback called when an index (row) is clicked
cellValues string[] | Element[] Values that will fill each Cell
className string Classname to be applied to the Row container
styles CSSStyles CSS Styles to be applied to the Row container

Cells

Prop Type Description
cellOverride Element | (props: CellProps, CellComponent: Cell) => Element Override for the row component

CellProps

Prop Type Description
onCellClick (cellValue: string | Element) => void Callback called when an cell is clicked
cellValue string | Element The value of the cell
className string Classname to be applied to the Cell container
styles CSSStyles CSS Styles to be applied to the Cell container

onIndexClick and onCellclick should act as middlewares rather than overriding our setup for opening the drawer, however, if the drawer is disabled those should act as overrides

Drawer

For Drawer, besides of having a drawerOverride component we should have a top-level disableDrawer prop so customers can easily just disable it when they require it without having to add a fallback with an empty result.

Prop Type Description
disableDrawer boolean When true, will disable the drawer
drawerOverride Element | (props: DrawerProps, Component: Drawer) => Element The override for the drawer component

DrawerProps

For v1, it would be enough that the Drawer just shows the data in the cell/row but in order to enable customization we’ll need to enable an onClose prop

Prop Type Description
onClose () => void Callback called when the close button in the drawer is clicked
displayValue string | Element Value that the Drawer would display
className string Classname to be applied to the Drawer container
style CSSStyles CSS Styles to be applied to the Drawer container

Pagination

Regarding pagination, we can provide high-level customization as well as low-level customization, for this, we most likely will want to change the rows per page options for now and then customers can fully customize the component with an override prop

Prop Type Description
pageSizeOptions number[] The page sizes options for pagination
paginationOverride Element | (props: PaginationProps, Component: Pagination) => Element The override for the pagination component
paginationBehavior 'infinite' | 'paginated' Determines whether the pagination should be performed as a virtualized infinite or a paginated way

The Pagination component should then be uncontrolled, so customers can easily call the setter and default value from props in their custom pagination component as well as the onNext and onBack

PaginationProps

Prop Type Description
setPageSize (pageSize: number) => void Setter to be called when the pageSize changes
defaultPageSize number The default pageSize for the paginator, should be one of pageSizeOptions otherwise should throw a warning
onNext () => void Callback to be called when the “next” button is clicked
onBack () => void Callback to be called when the “back” button is clicked
currentPage number The current page index
setCurrentPage (page: number) => void Setter to change the current page
hasNext boolean Whether a next page is available or not
hasPrevious boolean Whether a previous page is available or not
className string Classname to be applied to the Pagination container
style CSSStyles CSS Styles to be applied to the Pagination container

Then we would have Header which we could split in HeaderRow and HeaderCell those would be very similar to Row and Cell but should be distinct as it’s a common use case to customize the header different to the other rows/cells, additionally we should have a top-level prop to hide the header

Prop Type Description
hideHeader boolean If true, hides the header

Drawbacks

  • In order to provide a good level of customization it could require a lot of work, as DataGrid has a lot of components where we should enable customization.

Alternatives

  • Customers already have a useDataGrid hook that they could use in their own tables

Adoption strategy

  • DataGrid would be a new component, however, customers could be already using the useDataGrid hook, we could encourage them to start using the DataGrid component, however this is not a breaking change nor it has to be mandatory to migrate from useDataGrid to DataGrid component.

Unresolved questions

  • What component library to use, probably the best option is Tanstack Table, there are not so many good libraries for our use case
  • Is this too much for a v1? It is important to provide our customers with enough customization for their use cases which adds a fair amount of work and based on the library we select we may require more or less work on implementing the required features, should we shrink requirements for v1?
  • Pagination in static mode. In the current design we only consider connected mode for this, although customers could implement pagination on their own using the override, should we also care about supporting pagination in static mode for v1?
  • Should we enable downloading query data as .csv?
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant