diff --git a/lib/flop_phoenix.ex b/lib/flop_phoenix.ex index 8540482..0664eda 100644 --- a/lib/flop_phoenix.ex +++ b/lib/flop_phoenix.ex @@ -1025,6 +1025,12 @@ defmodule Flop.Phoenix do will not be clickable. """ + attr :directions, :any, + doc: """ + An optional 2-element tuple used for custom ascending and descending sort + behavior for the column, i.e. `{:asc_nulls_last, :desc_nulls_first}` + """ + attr :show, :boolean, doc: """ Boolean value to conditionally show the column. Defaults to `true` diff --git a/lib/flop_phoenix/table.ex b/lib/flop_phoenix/table.ex index 281e62b..4dd3783 100644 --- a/lib/flop_phoenix/table.ex +++ b/lib/flop_phoenix/table.ex @@ -81,6 +81,7 @@ defmodule Flop.Phoenix.Table do field={col[:field]} label={col[:label]} sortable={sortable?(col[:field], @meta.schema)} + directions={col[:directions]} meta={@meta} thead_th_attrs={ merge_attrs(@opts[:thead_th_attrs], col, :thead_th_attrs) @@ -173,6 +174,7 @@ defmodule Flop.Phoenix.Table do attr :target, :string, required: true attr :sortable, :boolean, required: true attr :thead_th_attrs, :list, required: true + attr :directions, :any attr :symbol_asc, :any attr :symbol_desc, :any attr :symbol_unsorted, :any @@ -183,11 +185,26 @@ defmodule Flop.Phoenix.Table do direction = order_direction(assigns.meta.flop, assigns.field) assigns = assign(assigns, :order_direction, direction) + sort_path_options = + if directions = assigns[:directions], + do: [directions: directions], + else: [] + + sort_path = + build_path( + assigns[:path], + assigns[:meta], + assigns[:field], + sort_path_options + ) + + assigns = assign(assigns, :sort_path, sort_path) + ~H""" <.sort_link - path={build_path(@path, @meta, @field)} + path={@sort_path} on_sort={@on_sort} event={@event} field={@field} @@ -295,12 +312,17 @@ defmodule Flop.Phoenix.Table do field in (module |> struct() |> Flop.Schema.sortable()) end - defp build_path(nil, _, _), do: nil + defp build_path(nil, _, _, _), do: nil - defp build_path(path, meta, field) do + defp build_path( + path, + meta, + field, + opts + ) do Flop.Phoenix.build_path( path, - Flop.push_order(meta.flop, field), + Flop.push_order(meta.flop, field, opts), backend: meta.backend, for: meta.schema ) diff --git a/mix.exs b/mix.exs index 2a7c5ea..2c1c8f5 100644 --- a/mix.exs +++ b/mix.exs @@ -55,7 +55,7 @@ defmodule FlopPhoenix.MixProject do {:ex_machina, "~> 2.4", only: :test}, {:excoveralls, "~> 0.10", only: :test}, {:floki, "~> 0.34.0", only: :test}, - {:flop, "~> 0.22.0"}, + {:flop, "~> 0.23.0"}, {:jason, "~> 1.0", only: [:dev, :test]}, {:makeup_diff, "~> 0.1.0", only: :dev, runtime: false}, {:phoenix, "~> 1.6.0 or ~> 1.7.0"}, diff --git a/mix.lock b/mix.lock index 1ca85ed..aa1e735 100644 --- a/mix.lock +++ b/mix.lock @@ -12,7 +12,7 @@ "excoveralls": {:hex, :excoveralls, "0.17.1", "83fa7906ef23aa7fc8ad7ee469c357a63b1b3d55dd701ff5b9ce1f72442b2874", [:mix], [{:castore, "~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "95bc6fda953e84c60f14da4a198880336205464e75383ec0f570180567985ae0"}, "file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"}, "floki": {:hex, :floki, "0.34.3", "5e2dcaec5d7c228ce5b1d3501502e308b2d79eb655e4191751a1fe491c37feac", [:mix], [], "hexpm", "9577440eea5b97924b4bf3c7ea55f7b8b6dce589f9b28b096cc294a8dc342341"}, - "flop": {:hex, :flop, "0.22.1", "4eb5dc1c159845e31d33cd8e73e249b84d013d3b9b1fb17619e70d55fbd2b025", [:mix], [{:ecto, "~> 3.10.3", [hex: :ecto, repo: "hexpm", optional: false]}, {:nimble_options, "~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}], "hexpm", "2f212238d92a8fcc2adadf20dcd875c6f8b4e1ce38ca6bc3f805918a5be567e4"}, + "flop": {:hex, :flop, "0.23.0", "68b07840df6dc6fc53682097f3838c8ea5b6806642c41651a2d5da85f7e676b1", [:mix], [{:ecto, "~> 3.10.3", [hex: :ecto, repo: "hexpm", optional: false]}, {:nimble_options, "~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}], "hexpm", "0521d0cbd58433607b9e9c77e065cc00eeb6ffdacd8fa1ed009366386f72d6c0"}, "jason": {:hex, :jason, "1.4.1", "af1504e35f629ddcdd6addb3513c3853991f694921b1b9368b0bd32beb9f1b63", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fbb01ecdfd565b56261302f7e1fcc27c4fb8f32d56eab74db621fc154604a7a1"}, "makeup": {:hex, :makeup, "1.1.0", "6b67c8bc2882a6b6a445859952a602afc1a41c2e08379ca057c0f525366fc3ca", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "0a45ed501f4a8897f580eabf99a2e5234ea3e75a4373c8a52824f6e873be57a6"}, "makeup_diff": {:hex, :makeup_diff, "0.1.0", "5be352b6aa6f07fa6a236e3efd7ba689a03f28fb5d35b7a0fa0a1e4a64f6d8bb", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "186bad5bb433a8afeb16b01423950e440072284a4103034ca899180343b9b4ac"}, diff --git a/test/flop_phoenix_test.exs b/test/flop_phoenix_test.exs index 1602dcf..32c36ab 100644 --- a/test/flop_phoenix_test.exs +++ b/test/flop_phoenix_test.exs @@ -2176,6 +2176,64 @@ defmodule Flop.PhoenixTest do assert Jason.decode!(phx_click) == [["push", %{"event" => "sort"}]] end + test "application of custom sort directions per column" do + assigns = %{ + meta: %Flop.Meta{ + flop: %Flop{ + order_by: [:ttfb], + order_directions: [:desc_nulls_last] + } + }, + items: [ + %{ + ttfb: 2 + }, + %{ + ttfb: 1 + }, + %{ + ttfb: nil + } + ], + ttfb_directions: {:asc_nulls_last, :desc_nulls_last} + } + + html = + ~H""" + + <:col + :let={navigation} + label="TTFB" + field={:ttfb} + directions={@ttfb_directions} + > + <%= navigation.ttfb %> + + + """ + |> rendered_to_string() + |> Floki.parse_fragment!() + + [ttfb_sort_href] = + html + |> Floki.find("thead th a:fl-contains('TTFB')") + |> Floki.attribute("href") + + %URI{query: query} = URI.parse(ttfb_sort_href) + decoded_query = Query.decode(query) + + # assert href representing opposite direction of initial table sort + assert %{ + "order_by" => ["ttfb"], + "order_directions" => ["asc_nulls_last"] + } = decoded_query + end + test "supports a function/args tuple as path" do html = render_table(%{path: {&route_helper/3, @route_helper_opts}}) assert [a] = Floki.find(html, "th a:fl-contains('Name')")