evidence-dev / evidence

Business intelligence as code: build fast, interactive data visualizations in pure SQL and markdown
https://evidence.dev
MIT License
4.23k stars 200 forks source link

Improved tables #468

Closed hughess closed 1 year ago

hughess commented 1 year ago

Background

We love tables - they’re often the best way to organize and present data, especially when combined with in-table graphics. We haven’t had a chance yet to do tables justice in Evidence, but we’re starting that process now.

This issue will document the features we’re hoping to eventually support in tables, as well as the architecture decisions to make those features possible.

Any feedback or commentary is welcome!

Long-Term Inspiration

We take inspiration from tables that use high display density and graphics to present information. Stephen Few has many great examples of this type of thinking. Publications like The Economist also do a good job of this.

Eventually, we’d like to make it easy to build tables like these using your own data in Evidence.

CleanShot 2022-11-16 at 09 32 57@2x CleanShot 2022-11-16 at 09 41 49@2x CleanShot 2022-11-16 at 09 42 04@2x

Feature Ideas

Not an exhaustive list, but covers the major ideas:

Immediate Needs

Below are the issues we currently have with our DataTable component:

Implementation

Using a library

There are several libraries we’ve looked into for building tables in JavaScript, including AG-Grid, GridJS, Datatables, and others.

In general, it seems that the available table libraries would cover our basic table needs, but would impose a ceiling on what we can build in the long-term. Things like column grouping, row grouping, and cell graphics are difficult to achieve in the libraries we’ve investigated.

Syntax

A lot of the features we want to offer are at the column-level, meaning we will need some way to configure options for each column. Below are a few options to accomplish this:

Column component

<Table data={my_query}>
   <Column name=date align=left/>
   <Column name=sales/>
</Table>
<Table data={my_query}>
   <Column name=sales>
      <Sparkline/>
   </Column>
</Table>

Array of columns

<Table data={my_query} columns={["date", "sales"]} align={{"date": "left"}}/>

Steps to Implement

  1. Solve the immediate issues we have in the DataTable component and replace DataTable with a more configurable table component
  2. Release a "presentation table" which uses a set of defaults to create a publication quality table
  3. Enable conditional formatting within table cells
  4. Expand our library of table graphics (sparklines, charts, symbols, etc.)
archiewood commented 1 year ago

Syntax

I expected to like the Array syntax more, as it is less boilerplate to pick specific columns:

Array

<Table data={my_query} columns={["date", "sales"]}/>

(52 chars)

vs

Column component

<Table data={my_query}>
   <Column name=date/>
   <Column name=sales/>
</Table>

(76 chars)

But as soon as try to customize the specific column, that benefit falls away. I also find the two types of double braces in the Array syntactically confusing.

The other thing that's nice about the Column component syntax is it's a bit clearer what's in the table.

How could we reduce the boilerplate in Column component?

archiewood commented 1 year ago

Thinking about this more, the column syntax is also more powerful for conditional logic. For example, in the scenario where you have loads of columns and want to exclude some:

<Table data={my_query_with_100_cols}>
    {#each Object.keys(my_query_with_100_cols[0]) as column} <!-- maybe we can improve this Object.keys() syntax-->
    {#if column != "the_column_to_exclude"}
    <Column name={column}/>
    {/if}
    {/each}
</Table>
roveo commented 1 year ago

I think in BI applications one of the most important things is the ability to make pivot tables, preferably nested, even more so than the ability to make nice tables for flat data.

E.g.

<PivotTable
  data={sales_stats}
  rows={["department", "salesperson"]}
  columns={["product_category"]}
  values-as="rows"
>
  <Value field="sales_amount" format="$" title="Sales Amount (YTD)" />
  <Value
    field="yoy_growth"
    format="%"
    title="YoY Growth in Sales"
    background-color="yoy_growth_sign"
  />
</PivotTable>

I don't know if this syntax would work with Svelte, but you get the idea.

Useful features that I can think of:

yukseltron commented 1 year ago

Offering options on how to make tables responsive would be interesting (horizontal scroll, grouped rows, etc).

Based on the data type/size, there are different ideas that could be used for displaying the tables. This short article shows some simple ways to achieve responsive and accessible table design, however, the examples aren't as advanced as having graphs in them. I think that could be an interesting challenge to explore.

archiewood commented 1 year ago

Another thing that could be great here. (Perhaps for our next round of iteration)

Support for some kind of summary row. In an example report I'm speccing out, It would be useful to include a total row as the final row.

This could be configurable at the column level

<DataTable
  data={myquery} 
  totalsRow=true>
 <Column id=partner/>
 <Column id=item/>
 <Column id=num_units agg=sum/>
 <Column id=cost_usd agg=sum/>
</DataTable>

Currently I'm doing it manually like this:

<DataTable data={myquery}/>
<div class='invoice-total'>
<div>
    Total Cost: 
</div>
<div>
    <Value data={myquery} column =cost_usd />
</div>
</div>
image
hughess commented 1 year ago

@archiewood that’s a great idea. How would you expect totals to work in a table that’s paginated?

archiewood commented 1 year ago

Great question. Hadn’t thought about it.

I would expect it to sum across everything including invisible rows I think.

But I’d rarely implement this row if there was pagination, and I think I’d keep it off by default

mcrascal commented 1 year ago

This is fantastic. Going to close in favour of individual issues for the big ideas in here:

  1. Pivot tables #596
  2. Expanding table graphics: #744 and #588
  3. Colouring table cells #679
  4. Arbitrary content for table cells #570
  5. Syntax #694