Repository: woylie/flop_phoenix (elixir)

Issue:
Overriding attrs for table heading
Hi Woylie,

I can override `tbody_td_attrs` to align some numbers to the right by passing `attrs={[class: "text-right ..."]}` to `:col` but there is no option to do the same with `thead_th_attrs`.

I also tried `attrs={[align: "right"]}`, it works for `td` but not for `th`.


Pull request description:
Override table heading attrs
**Description**

Issue #263 - Overriding attrs for table heading 

* Renamed `table.col.attrs` to `table.col.td_attrs`
* Added `table.col.th_attrs` to override `table.thead_th_attrs`
* Added `table.col.th_wrapper_attrs` to override `table.th_wrapper_attrs`

**Checklist**

- [x] I added tests that cover my proposed changes.
- [x] I updated the documentation related to my changes (if applicable).

resolves #263 


Patch:
diff --git a/lib/flop_phoenix.ex b/lib/flop_phoenix.ex
index bc7d4a9..8540482 100644
--- a/lib/flop_phoenix.ex
+++ b/lib/flop_phoenix.ex
@@ -1045,7 +1045,22 @@ defmodule Flop.Phoenix do
       of a column this way.
       """
 
-    attr :attrs, :any,
+    attr :thead_th_attrs, :list,
+      doc: """
+      Additional attributes to pass to the `<th>` element as a static keyword
+      list. Note that these attributes will override any conflicting
+      `thead_th_attrs` that are set at the table level.
+      """
+
+    attr :th_wrapper_attrs, :list,
+      doc: """
+      Additional attributes for the `<span>` element that wraps the
+      header link and the order direction symbol. Note that these attributes
+      will override any conflicting `th_wrapper_attrs` that are set at the table
+      level.
+      """
+
+    attr :tbody_td_attrs, :any,
       doc: """
       Additional attributes to pass to the `<td>` element. May be provided as a
       static keyword list, or as a 1-arity function to dynamically generate the
@@ -1083,7 +1098,12 @@ defmodule Flop.Phoenix do
       of a column this way.
       """
 
-    attr :attrs, :any,
+    attr :thead_th_attrs, :list,
+      doc: """
+      Any additional attributes to pass to the `<th>` as a keyword list.
+      """
+
+    attr :tbody_td_attrs, :any,
       doc: """
       Any additional attributes to pass to the `<td>`. Can be a keyword list or
       a function that takes the current row item as an argument and returns a
diff --git a/lib/flop_phoenix/table.ex b/lib/flop_phoenix/table.ex
index 58155f4..281e62b 100644
--- a/lib/flop_phoenix/table.ex
+++ b/lib/flop_phoenix/table.ex
@@ -82,12 +82,16 @@ defmodule Flop.Phoenix.Table do
             label={col[:label]}
             sortable={sortable?(col[:field], @meta.schema)}
             meta={@meta}
-            thead_th_attrs={@opts[:thead_th_attrs]}
+            thead_th_attrs={
+              merge_attrs(@opts[:thead_th_attrs], col, :thead_th_attrs)
+            }
             symbol_asc={@opts[:symbol_asc]}
             symbol_desc={@opts[:symbol_desc]}
             symbol_unsorted={@opts[:symbol_unsorted]}
             symbol_attrs={@opts[:symbol_attrs]}
-            th_wrapper_attrs={@opts[:th_wrapper_attrs]}
+            th_wrapper_attrs={
+              merge_attrs(@opts[:th_wrapper_attrs], col, :th_wrapper_attrs)
+            }
             path={@path}
             target={@target}
           />
@@ -99,7 +103,9 @@ defmodule Flop.Phoenix.Table do
             label={action[:label]}
             sortable={false}
             meta={@meta}
-            thead_th_attrs={@opts[:thead_th_attrs]}
+            thead_th_attrs={
+              merge_attrs(@opts[:thead_th_attrs], action, :thead_th_attrs)
+            }
             path={nil}
             target={@event}
           />
@@ -136,8 +142,15 @@ defmodule Flop.Phoenix.Table do
     """
   end
 
+  defp merge_attrs(base_attrs, col, key) when is_atom(key) do
+    attrs = Map.get(col, key, [])
+    Keyword.merge(base_attrs, attrs)
+  end
+
   defp merge_td_attrs(tbody_td_attrs, col, item) do
-    attrs = col |> Map.get(:attrs, []) |> maybe_invoke_options_callback(item)
+    attrs =
+      col |> Map.get(:tbody_td_attrs, []) |> maybe_invoke_options_callback(item)
+
     Keyword.merge(tbody_td_attrs, attrs)
   end
 


Test patch:
diff --git a/test/flop_phoenix_test.exs b/test/flop_phoenix_test.exs
index aab00d4..1602dcf 100644
--- a/test/flop_phoenix_test.exs
+++ b/test/flop_phoenix_test.exs
@@ -1782,7 +1782,7 @@ defmodule Flop.PhoenixTest do
       assert [_] = Floki.find(html, "tr.crime-reporter")
     end
 
-    test "evaluates attrs function for col slot / td" do
+    test "evaluates tbody_td_attrs function for col slot / td" do
       assigns = %{}
 
       html =
@@ -1795,7 +1795,7 @@ defmodule Flop.PhoenixTest do
           ]}
           meta={%Flop.Meta{flop: %Flop{}}}
         >
-          <:col attrs={
+          <:col tbody_td_attrs={
             fn item ->
               [class: if(item.age > 17, do: "adult", else: "child")]
             end
@@ -1808,7 +1808,7 @@ defmodule Flop.PhoenixTest do
       assert [_] = Floki.find(html, "td.child")
     end
 
-    test "evaluates attrs function in action columns" do
+    test "evaluates tbody_td_attrs function in action columns" do
       assigns = %{
         attrs_fun: fn item ->
           [class: if(item.age > 17, do: "adult", else: "child")]
@@ -1826,7 +1826,7 @@ defmodule Flop.PhoenixTest do
           on_sort={%JS{}}
         >
           <:col :let={u} label="Name"><%= u.name %></:col>
-          <:action label="Buttons" attrs={@attrs_fun}>some action</:action>
+          <:action label="Buttons" tbody_td_attrs={@attrs_fun}>some action</:action>
         </Flop.Phoenix.table>
         """)
 
@@ -1853,6 +1853,33 @@ defmodule Flop.PhoenixTest do
       assert [_, _] = Floki.find(html, "td.tolerance")
     end
 
+    test "adds additional attributes to th" do
+      assigns = %{}
+
+      html =
+        parse_heex(~H"""
+        <Flop.Phoenix.table
+          path="/pets"
+          items={[%{name: "George", age: 8}]}
+          meta={%Flop.Meta{flop: %Flop{}}}
+        >
+          <:col :let={pet} thead_th_attrs={[class: "name-header"]}>
+            <%= pet.name %>
+          </:col>
+          <:col :let={pet} thead_th_attrs={[class: "age-header"]}>
+            <%= pet.age %>
+          </:col>
+          <:action :let={pet} thead_th_attrs={[class: "action-header"]}>
+            <.link navigate={"/show/pet/#{pet.name}"}>Show Pet</.link>
+          </:action>
+        </Flop.Phoenix.table>
+        """)
+
+      assert [_] = Floki.find(html, "th.name-header")
+      assert [_] = Floki.find(html, "th.age-header")
+      assert [_] = Floki.find(html, "th.action-header")
+    end
+
     test "adds additional attributes to td" do
       assigns = %{}
 
@@ -1863,13 +1890,13 @@ defmodule Flop.PhoenixTest do
           items={[%{name: "George", age: 8}]}
           meta={%Flop.Meta{flop: %Flop{}}}
         >
-          <:col :let={pet} attrs={[class: "name-column"]}>
+          <:col :let={pet} tbody_td_attrs={[class: "name-column"]}>
             <%= pet.name %>
           </:col>
-          <:col :let={pet} attrs={[class: "age-column"]}>
+          <:col :let={pet} tbody_td_attrs={[class: "age-column"]}>
             <%= pet.age %>
           </:col>
-          <:action :let={pet} attrs={[class: "action-column"]}>
+          <:action :let={pet} tbody_td_attrs={[class: "action-column"]}>
             <.link navigate={"/show/pet/#{pet.name}"}>Show Pet</.link>
           </:action>
         </Flop.Phoenix.table>
@@ -1880,7 +1907,80 @@ defmodule Flop.PhoenixTest do
       assert [_] = Floki.find(html, "td.action-column")
     end
 
-    test "overrides table_td_attrs with td attrs" do
+    test "overrides table_th_attrs with thead_th_attrs in col" do
+      assigns = %{
+        meta: %Flop.Meta{flop: %Flop{}, schema: MyApp.Pet},
+        items: [%{name: "George", age: 8}],
+        opts: [thead_th_attrs: [class: "default-th-class"]]
+      }
+
+      html =
+        parse_heex(~H"""
+        <Flop.Phoenix.table items={@items} meta={@meta} on_sort={%JS{}} opts={@opts}>
+          <:col :let={i} thead_th_attrs={[class: "name-th-class"]}><%= i.name %></:col>
+          <:col :let={i}><%= i.age %></:col>
+        </Flop.Phoenix.table>
+        """)
+
+      assert [{"th", [{"class", "name-th-class"}], _}] =
+               Floki.find(html, "th:first-child")
+
+      assert [{"th", [{"class", "default-th-class"}], _}] =
+               Floki.find(html, "th:last-child")
+    end
+
+    test "evaluates table th_wrapper_attrs" do
+      assigns = %{
+        meta: %Flop.Meta{flop: %Flop{}, schema: MyApp.Pet},
+        items: [%{name: "George", age: 8}],
+        opts: [th_wrapper_attrs: [class: "default-th-wrapper-class"]]
+      }
+
+      html =
+        parse_heex(~H"""
+        <Flop.Phoenix.table items={@items} meta={@meta} on_sort={%JS{}} opts={@opts}>
+          <:col :let={i} field={:name}><%= i.name %></:col>
+          <:col :let={i}><%= i.age %></:col>
+        </Flop.Phoenix.table>
+        """)
+
+      assert [
+               {"th", [],
+                [{"span", [{"class", "default-th-wrapper-class"}], _}]}
+             ] = Floki.find(html, "th:first-child")
+    end
+
+    test "overrides th_wrapper_attrs" do
+      assigns = %{
+        meta: %Flop.Meta{flop: %Flop{}, schema: MyApp.Pet},
+        items: [%{name: "George", age: 8}],
+        opts: [th_wrapper_attrs: [class: "default-th-wrapper-class"]]
+      }
+
+      html =
+        parse_heex(~H"""
+        <Flop.Phoenix.table items={@items} meta={@meta} on_sort={%JS{}} opts={@opts}>
+          <:col
+            :let={i}
+            field={:name}
+            th_wrapper_attrs={[class: "name-th-wrapper-class"]}
+          >
+            <%= i.name %>
+          </:col>
+          <:col :let={i} field={:age}><%= i.age %></:col>
+        </Flop.Phoenix.table>
+        """)
+
+      assert [{"th", [], [{"span", [{"class", "name-th-wrapper-class"}], _}]}] =
+               Floki.find(html, "th:first-child")
+
+      assert [
+               {"th", [],
+                [{"span", [{"class", "default-th-wrapper-class"}], _}]}
+             ] = Floki.find(html, "th:last-child")
+    end
+
+    test "overrides table_td_attrs with tbody_td_attrs in col" do
       assigns = %{
         meta: %Flop.Meta{flop: %Flop{}, schema: MyApp.Pet},
         items: [%{name: "George", age: 8}],
@@ -1890,7 +1990,7 @@ defmodule Flop.PhoenixTest do
       html =
         parse_heex(~H"""
         <Flop.Phoenix.table items={@items} meta={@meta} on_sort={%JS{}} opts={@opts}>
-          <:col :let={i} attrs={[class: "name-td-class"]}><%= i.name %></:col>
+          <:col :let={i} tbody_td_attrs={[class: "name-td-class"]}><%= i.name %></:col>
           <:col :let={i}><%= i.age %></:col>
         </Flop.Phoenix.table>
         """)
@@ -1902,7 +2002,30 @@ defmodule Flop.PhoenixTest do
                Floki.find(html, "td:last-child")
     end
 
-    test "overrides table_td_attrs with td attrs in action columns" do
+    test "overrides table_th_attrs with thead_th_attrs in action columns" do
+      assigns = %{
+        meta: %Flop.Meta{flop: %Flop{}, schema: MyApp.Pet},
+        items: [%{name: "George", age: 8}],
+        opts: [thead_th_attrs: [class: "default-th-class"]]
+      }
+
+      html =
+        parse_heex(~H"""
+        <Flop.Phoenix.table items={@items} meta={@meta} on_sort={%JS{}} opts={@opts}>
+          <:col :let={i}><%= i.name %></:col>
+          <:action thead_th_attrs={[class: "action-1-th-class"]}>action 1</:action>
+          <:action>action 2</:action>
+        </Flop.Phoenix.table>
+        """)
+
+      assert [{"th", [{"class", "action-1-th-class"}], _}] =
+               Floki.find(html, "th:nth-child(2)")
+
+      assert [{"th", [{"class", "default-th-class"}], _}] =
+               Floki.find(html, "th:last-child")
+    end
+
+    test "overrides table_td_attrs with tbody_td_attrs in action columns" do
       assigns = %{
         meta: %Flop.Meta{flop: %Flop{}, schema: MyApp.Pet},
         items: [%{name: "George", age: 8}],
@@ -1913,7 +2036,7 @@ defmodule Flop.PhoenixTest do
         parse_heex(~H"""
         <Flop.Phoenix.table items={@items} meta={@meta} on_sort={%JS{}} opts={@opts}>
           <:col :let={i}><%= i.name %></:col>
-          <:action attrs={[class: "action-1-td-class"]}>action 1</:action>
+          <:action tbody_td_attrs={[class: "action-1-td-class"]}>action 1</:action>
           <:action>action 2</:action>
         </Flop.Phoenix.table>
         """)