Repository: woylie/flop_phoenix
Pull request #327 (2024-03-13 10:21:15)

Add support for :col_class on cols
Hi @woylie,

I'm currently adding a relatively strict CSP header to one of our apps (i.e. no `unsafe-inline`) and the inline `style` attributes on the `colgroup` elements are violating the policy. With this patch, I'd be able to set the styles via classes instead.

---

This patch adds support for a new `:col_class` prop on `:col` and `:action` slots for `table/1` which sets the `class` attribute on the generated `colgroup`. Additionally it removes empty `style=""` attributes.

Both of these changes are intended to prevent inline `style` attributes violating strict content-security-policies.

    <:col col_style="width: 20rem;"></:col>
    <:col col_class="some-class"></:col>
    <:col></:col>

... renders the following colgroup:

    <colgroup>
      <col style="width: 20rem"></col>
      <col class="some-class"></col>
      <col></col>
    </colgroup>


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a5b2520..901685b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,14 @@
 
 ## Unreleased
 
+### Added
+
+- Support `:col_class` attr on `:col` and `:action` slots in addition to `:col_style`.
+
+### Changed
+
+- Don't render empty `style` attributes on `col` elements in `colgroup`.
+
 ## [0.22.7] - 2024-03-02
 
 ### Changed
diff --git a/lib/flop_phoenix.ex b/lib/flop_phoenix.ex
index 4eb75c9..0a7fe58 100644
--- a/lib/flop_phoenix.ex
+++ b/lib/flop_phoenix.ex
@@ -1064,8 +1064,16 @@ defmodule Flop.Phoenix do
       doc: """
       If set, a `<colgroup>` element is rendered and the value of the
       `col_style` assign is set as `style` attribute for the `<col>` element of
-      the respective column. You can set the `width`, `background` and `border`
-      of a column this way.
+      the respective column. You can set the `width`, `background`, `border`,
+      and `visibility` of a column this way.
+      """
+
+    attr :col_class, :string,
+      doc: """
+      If set, a `<colgroup>` element is rendered and the value of the
+      `col_class` assign is set as `class` attribute for the `<col>` element of
+      the respective column. You can set the `width`, `background`, `border`,
+      and `visibility` of a column this way.
       """
 
     attr :thead_th_attrs, :list,
@@ -1117,8 +1125,16 @@ defmodule Flop.Phoenix do
       doc: """
       If set, a `<colgroup>` element is rendered and the value of the
       `col_style` assign is set as `style` attribute for the `<col>` element of
-      the respective column. You can set the `width`, `background` and `border`
-      of a column this way.
+      the respective column. You can set the `width`, `background`, `border`,
+      and `visibility` of a column this way.
+      """
+
+    attr :col_class, :string,
+      doc: """
+      If set, a `<colgroup>` element is rendered and the value of the
+      `col_class` assign is set as `class` attribute for the `<col>` element of
+      the respective column. You can set the `width`, `background`, `border`,
+      and `visibility` of a column this way.
       """
 
     attr :thead_th_attrs, :list,
diff --git a/lib/flop_phoenix/table.ex b/lib/flop_phoenix/table.ex
index 0144ce4..e893b3c 100644
--- a/lib/flop_phoenix/table.ex
+++ b/lib/flop_phoenix/table.ex
@@ -60,16 +60,7 @@ defmodule Flop.Phoenix.Table do
     ~H"""
     <table id={@id} {@opts[:table_attrs]}>
       <caption :if={@caption}><%= @caption %></caption>
-      <colgroup :if={
-        Enum.any?(@col, & &1[:col_style]) or Enum.any?(@action, & &1[:col_style])
-      }>
-        <col :for={col <- @col} :if={show_column?(col)} style={col[:col_style]} />
-        <col
-          :for={action <- @action}
-          :if={show_column?(action)}
-          style={action[:col_style]}
-        />
-      </colgroup>
+      <.maybe_colgroup col={@col ++ @action} />
       <thead {@opts[:thead_attrs]}>
         <tr {@opts[:thead_tr_attrs]}>
           <.header_column
@@ -160,6 +151,22 @@ defmodule Flop.Phoenix.Table do
 
   defp maybe_invoke_options_callback(option, _item), do: option
 
+  defp maybe_colgroup(assigns) do
+    ~H"""
+    <colgroup :if={Enum.any?(@col, &(&1[:col_style] || &1[:col_class]))}>
+      <col
+        :for={col <- @col}
+        :if={show_column?(col)}
+        {reject_empty_values(style: col[:col_style], class: col[:col_class])}
+      />
+    </colgroup>
+    """
+  end
+
+  defp reject_empty_values(attrs) do
+    Enum.reject(attrs, fn {_, v} -> v in ["", nil] end)
+  end
+
   defp show_column?(%{hide: true}), do: false
   defp show_column?(%{show: false}), do: false
   defp show_column?(_), do: true


Test patch:
diff --git a/test/flop_phoenix_test.exs b/test/flop_phoenix_test.exs
index bf96575..f88250f 100644
--- a/test/flop_phoenix_test.exs
+++ b/test/flop_phoenix_test.exs
@@ -2666,13 +2666,16 @@ defmodule Flop.PhoenixTest do
         <Flop.Phoenix.table
           event="sort"
           id="user-table"
-          items={[%{name: "George", age: 8}]}
+          items={[%{name: "George", surname: "Floyd", age: 8}]}
           meta={%Flop.Meta{flop: %Flop{}}}
         >
           <:col :let={pet} label="Name" field={:name} col_style="width: 60%;">
             <%= pet.name %>
           </:col>
-          <:col :let={pet} label="Age" field={:age} col_style="width: 40%;">
+          <:col :let={pet} label="Surname" field={:surname}>
+            <%= pet.surname %>
+          </:col>
+          <:col :let={pet} label="Age" field={:age} col_class="some-col-class">
             <%= pet.age %>
           </:col>
         </Flop.Phoenix.table>
@@ -2684,7 +2687,8 @@ defmodule Flop.PhoenixTest do
                   {"colgroup", _,
                    [
                      {"col", [{"style", "width: 60%;"}], _},
-                     {"col", [{"style", "width: 40%;"}], _}
+                     {"col", [], _},
+                     {"col", [{"class", "some-col-class"}], _}
                    ]},
                   {"thead", _, _},
                   {"tbody", _, _}