Repository: livebook-dev/kino (elixir)

Issue:
Kino.DataTable does not preserve the order of columns when specified with option `:keys`
The documentation of [`Kino.DataTable.new/2`](https://hexdocs.pm/kino/Kino.DataTable.html#new/2) mentions that the keys also dictate the order of the rendered columns:

> :keys - a list of keys to include in the table for each record. _The order is reflected in the rendered table._ Optional

However, the order of keys does not seem to be reflected in the table. Using this simple example shows the issue:

```elixir
data = [%{one: 1, two: 2}, %{one: 3, two: 4}]
keys = [:two, :one]
Kino.DataTable.new(data, keys: keys)
```

Which renders the table with columns `:one` and `:two` (and not in the expected order `:two` and `:one`).

Looking at the implementation, the delegation to [`Table.to_rows_with_info/2`](https://hexdocs.pm/table/0.1.0/Table.html#to_rows_with_info/2) makes no guarantees about the order of columns.

Either the documentation promises too much, or the order should be preserved somehow.

Pull request description:
Adjust DataTable to respect the :keys order
Closes #150.

Sidenote: `Table.to_rows_with_info/2` always returns all columns in `info`, but I think info is more about the table itself, rather than the specific rows. @josevalim wdyt?

Patch:
diff --git a/lib/kino/data_table.ex b/lib/kino/data_table.ex
index bc3f546..c771fe0 100644
--- a/lib/kino/data_table.ex
+++ b/lib/kino/data_table.ex
@@ -50,11 +50,14 @@ defmodule Kino.DataTable do
     name = Keyword.get(opts, :name, "Data")
     sorting_enabled = Keyword.get(opts, :sorting_enabled, true)
 
-    {data_rows, %{columns: data_columns}} =
+    {data_rows, data_columns} =
       if keys = opts[:keys] do
-        Table.to_rows_with_info(tabular, only: keys)
+        {rows, %{columns: columns}} = Table.to_rows_with_info(tabular, only: keys)
+        nonexistent = keys -- columns
+        {rows, keys -- nonexistent}
       else
-        Table.to_rows_with_info(tabular)
+        {rows, %{columns: columns}} = Table.to_rows_with_info(tabular)
+        {rows, columns}
       end
 
     Kino.Table.new(__MODULE__, {data_rows, data_columns, name, sorting_enabled})


Test patch:
diff --git a/test/kino/data_table_test.exs b/test/kino/data_table_test.exs
index e2b5fd8..5e37ac2 100644
--- a/test/kino/data_table_test.exs
+++ b/test/kino/data_table_test.exs
@@ -117,6 +117,20 @@ defmodule Kino.DataTableTest do
            ]
   end
 
+  test "respects :keys order" do
+    kino = Kino.DataTable.new(@people_entries, keys: [:name, :id])
+    data = connect(kino)
+
+    assert %{
+             content: %{
+               columns: [
+                 %{key: "0", label: ":name"},
+                 %{key: "1", label: ":id"}
+               ]
+             }
+           } = data
+  end
+
   test "preserves data order by default" do
     kino = Kino.DataTable.new(@people_entries)
     data = connect(kino)