|
| 1 | +defmodule Phoenix.LiveViewTest.E2E.KeyedComprehensionLive do |
| 2 | + use Phoenix.LiveView |
| 3 | + |
| 4 | + @count 10 |
| 5 | + |
| 6 | + def render(assigns) do |
| 7 | + ~H""" |
| 8 | + <link href="https://cdn.jsdelivr.net/npm/daisyui@5" rel="stylesheet" type="text/css" /> |
| 9 | + <div class="p-8"> |
| 10 | + <div class="border-b border-gray-200 mb-6"> |
| 11 | + <nav role="tablist" class="tabs tabs-border"> |
| 12 | + <.link |
| 13 | + role="tab" |
| 14 | + class={"tab #{if @active_tab == "all_keyed", do: "tab-active"}"} |
| 15 | + patch="/keyed-comprehension?tab=all_keyed" |
| 16 | + > |
| 17 | + All keyed |
| 18 | + </.link> |
| 19 | + <.link |
| 20 | + role="tab" |
| 21 | + class={"tab #{if @active_tab == "rows_keyed", do: "tab-active"}"} |
| 22 | + patch="/keyed-comprehension?tab=rows_keyed" |
| 23 | + > |
| 24 | + Rows keyed |
| 25 | + </.link> |
| 26 | + <.link |
| 27 | + role="tab" |
| 28 | + class={"tab #{if @active_tab == "no_keyed", do: "tab-active"}"} |
| 29 | + patch="/keyed-comprehension?tab=no_keyed" |
| 30 | + > |
| 31 | + No keyed |
| 32 | + </.link> |
| 33 | + </nav> |
| 34 | + </div> |
| 35 | +
|
| 36 | + <button class="btn" phx-click="randomize">randomize</button> |
| 37 | + <button class="btn" phx-click="change_0">change first</button> |
| 38 | + <button class="btn" phx-click="change_other">change other</button> |
| 39 | +
|
| 40 | + <form> |
| 41 | + <input phx-change="change_size" name="size" value={@size} /> |
| 42 | + </form> |
| 43 | +
|
| 44 | + <div :for={i <- 1..2} :key={i}> |
| 45 | + <.table_with_all_keyed |
| 46 | + :if={@active_tab == "all_keyed"} |
| 47 | + rows={@items} |
| 48 | + id={fn row -> row.id end} |
| 49 | + > |
| 50 | + <:col :let={%{entry: entry}} id="1" name="Foo"> |
| 51 | + <.my_component my_count={@count} the_name={entry.foo.bar} /> {i} |
| 52 | + </:col> |
| 53 | + <:col id="2" name="Count">{@count}</:col> |
| 54 | + </.table_with_all_keyed> |
| 55 | +
|
| 56 | + <.table_with_rows_keyed |
| 57 | + :if={@active_tab == "rows_keyed"} |
| 58 | + rows={@items} |
| 59 | + id={fn row -> row.id end} |
| 60 | + > |
| 61 | + <:col :let={%{entry: entry}} id="1" name="Foo"> |
| 62 | + <.my_component my_count={@count} the_name={entry.foo.bar} /> {i} |
| 63 | + </:col> |
| 64 | + <:col id="2" name="Count">{@count}</:col> |
| 65 | + </.table_with_rows_keyed> |
| 66 | +
|
| 67 | + <.table_with_no_keyed :if={@active_tab == "no_keyed"} rows={@items} id={fn row -> row.id end}> |
| 68 | + <:col :let={%{entry: entry}} id="1" name="Foo"> |
| 69 | + <.my_component my_count={@count} the_name={entry.foo.bar} /> {i} |
| 70 | + </:col> |
| 71 | + <:col id="2" name="Count">{@count}</:col> |
| 72 | + </.table_with_no_keyed> |
| 73 | + </div> |
| 74 | + </div> |
| 75 | + """ |
| 76 | + end |
| 77 | + |
| 78 | + defp my_component(assigns) do |
| 79 | + ~H""" |
| 80 | + <span> |
| 81 | + Count: {@my_count} Name: {@the_name} |
| 82 | + </span> |
| 83 | + """ |
| 84 | + end |
| 85 | + |
| 86 | + attr :rows, :list, required: true |
| 87 | + slot :col |
| 88 | + |
| 89 | + defp table_with_all_keyed(assigns) do |
| 90 | + ~H""" |
| 91 | + <div class="mt-8 flow-root"> |
| 92 | + <div class="-mx-4 -my-2 overflow-x-auto sm:-mx-6 lg:-mx-8"> |
| 93 | + <div class="inline-block min-w-full py-2 align-middle sm:px-6 lg:px-8"> |
| 94 | + <table class="min-w-full divide-y divide-gray-300"> |
| 95 | + <thead> |
| 96 | + <tr> |
| 97 | + <th |
| 98 | + :for={slot <- @col} |
| 99 | + :key={slot.id} |
| 100 | + scope="col" |
| 101 | + class="py-3.5 first:pr-3 first:pl-4 px-3 text-left text-sm font-semibold text-gray-900 first:sm:pl-0" |
| 102 | + > |
| 103 | + {slot.name} |
| 104 | + </th> |
| 105 | + </tr> |
| 106 | + </thead> |
| 107 | + <tbody class="divide-y divide-gray-200"> |
| 108 | + <tr :for={row <- @rows} :key={@id.(row)}> |
| 109 | + <td |
| 110 | + :for={slot <- @col} |
| 111 | + :key={"#{@id.(row)}_#{slot.id}"} |
| 112 | + class="py-4 first:pr-3 first:pl-4 px-3 text-sm first:font-medium whitespace-nowrap first:text-gray-900 text-gray-500 first:sm:pl-0" |
| 113 | + > |
| 114 | + {render_slot(slot, row)} |
| 115 | + </td> |
| 116 | + </tr> |
| 117 | + </tbody> |
| 118 | + </table> |
| 119 | + </div> |
| 120 | + </div> |
| 121 | + </div> |
| 122 | + """ |
| 123 | + end |
| 124 | + |
| 125 | + attr :rows, :list, required: true |
| 126 | + slot :col |
| 127 | + |
| 128 | + defp table_with_rows_keyed(assigns) do |
| 129 | + ~H""" |
| 130 | + <div class="mt-8 flow-root"> |
| 131 | + <div class="-mx-4 -my-2 overflow-x-auto sm:-mx-6 lg:-mx-8"> |
| 132 | + <div class="inline-block min-w-full py-2 align-middle sm:px-6 lg:px-8"> |
| 133 | + <table class="min-w-full divide-y divide-gray-300"> |
| 134 | + <thead> |
| 135 | + <tr> |
| 136 | + <th |
| 137 | + :for={slot <- @col} |
| 138 | + scope="col" |
| 139 | + class="py-3.5 first:pr-3 first:pl-4 px-3 text-left text-sm font-semibold text-gray-900 first:sm:pl-0" |
| 140 | + > |
| 141 | + {slot.name} |
| 142 | + </th> |
| 143 | + </tr> |
| 144 | + </thead> |
| 145 | + <tbody class="divide-y divide-gray-200"> |
| 146 | + <tr :for={row <- @rows} :key={@id.(row)}> |
| 147 | + <td |
| 148 | + :for={slot <- @col} |
| 149 | + class="py-4 first:pr-3 first:pl-4 px-3 text-sm first:font-medium whitespace-nowrap first:text-gray-900 text-gray-500 first:sm:pl-0" |
| 150 | + > |
| 151 | + {render_slot(slot, row)} |
| 152 | + </td> |
| 153 | + </tr> |
| 154 | + </tbody> |
| 155 | + </table> |
| 156 | + </div> |
| 157 | + </div> |
| 158 | + </div> |
| 159 | + """ |
| 160 | + end |
| 161 | + |
| 162 | + attr :rows, :list, required: true |
| 163 | + slot :col |
| 164 | + |
| 165 | + defp table_with_no_keyed(assigns) do |
| 166 | + ~H""" |
| 167 | + <div class="mt-8 flow-root"> |
| 168 | + <div class="-mx-4 -my-2 overflow-x-auto sm:-mx-6 lg:-mx-8"> |
| 169 | + <div class="inline-block min-w-full py-2 align-middle sm:px-6 lg:px-8"> |
| 170 | + <table class="min-w-full divide-y divide-gray-300"> |
| 171 | + <thead> |
| 172 | + <tr> |
| 173 | + <th |
| 174 | + :for={slot <- @col} |
| 175 | + scope="col" |
| 176 | + class="py-3.5 first:pr-3 first:pl-4 px-3 text-left text-sm font-semibold text-gray-900 first:sm:pl-0" |
| 177 | + > |
| 178 | + {slot.name} |
| 179 | + </th> |
| 180 | + </tr> |
| 181 | + </thead> |
| 182 | + <tbody class="divide-y divide-gray-200"> |
| 183 | + <tr :for={row <- @rows}> |
| 184 | + <td |
| 185 | + :for={slot <- @col} |
| 186 | + class="py-4 first:pr-3 first:pl-4 px-3 text-sm first:font-medium whitespace-nowrap first:text-gray-900 text-gray-500 first:sm:pl-0" |
| 187 | + > |
| 188 | + {render_slot(slot, row)} |
| 189 | + </td> |
| 190 | + </tr> |
| 191 | + </tbody> |
| 192 | + </table> |
| 193 | + </div> |
| 194 | + </div> |
| 195 | + </div> |
| 196 | + """ |
| 197 | + end |
| 198 | + |
| 199 | + def mount(_params, _session, socket) do |
| 200 | + :timer.send_interval(1000, :report_memory) |
| 201 | + {:ok, assign(socket, count: 0, items: random_items(@count), size: @count, tailwind: true)} |
| 202 | + end |
| 203 | + |
| 204 | + def handle_params(params, _session, socket) do |
| 205 | + {:noreply, assign_tab(socket, params)} |
| 206 | + end |
| 207 | + |
| 208 | + defp assign_tab(socket, %{"tab" => tab}) when tab in ["all_keyed", "rows_keyed", "no_keyed"] do |
| 209 | + assign(socket, :active_tab, tab) |
| 210 | + end |
| 211 | + |
| 212 | + defp assign_tab(socket, _), do: assign(socket, :active_tab, "all_keyed") |
| 213 | + |
| 214 | + def handle_event("randomize", _params, socket) do |
| 215 | + {:noreply, |
| 216 | + socket |> assign(:items, random_items(socket.assigns.size)) |> update(:count, &(&1 + 1))} |
| 217 | + end |
| 218 | + |
| 219 | + def handle_event("change_size", %{"size" => size}, socket) do |
| 220 | + size = |
| 221 | + case size do |
| 222 | + "" -> 0 |
| 223 | + _ -> String.to_integer(size) |
| 224 | + end |
| 225 | + |
| 226 | + {:noreply, |
| 227 | + socket |
| 228 | + |> assign(:items, random_items(size)) |
| 229 | + |> assign(:size, size) |
| 230 | + |> update(:count, &(&1 + 1))} |
| 231 | + end |
| 232 | + |
| 233 | + def handle_event("change_0", _params, socket) do |
| 234 | + {:noreply, |
| 235 | + socket |
| 236 | + |> assign(:items, [ |
| 237 | + %{id: 2000, entry: %{other: "hey", foo: %{bar: "#{System.unique_integer()}"}}} |
| 238 | + | Enum.slice(socket.assigns.items, 1..(socket.assigns.size + 1)) |
| 239 | + ])} |
| 240 | + end |
| 241 | + |
| 242 | + def handle_event("change_other", _params, socket) do |
| 243 | + {:noreply, |
| 244 | + socket |
| 245 | + |> assign( |
| 246 | + :items, |
| 247 | + Enum.map(socket.assigns.items, fn item -> |
| 248 | + %{item | entry: %{item.entry | other: "hey #{System.unique_integer()}"}} |
| 249 | + end) |
| 250 | + )} |
| 251 | + end |
| 252 | + |
| 253 | + def handle_info(:report_memory, socket) do |
| 254 | + :erlang.garbage_collect() |
| 255 | + IO.puts("Heap size: #{Process.info(self())[:total_heap_size]}") |
| 256 | + |
| 257 | + {:noreply, socket} |
| 258 | + end |
| 259 | + |
| 260 | + def random_items(size) do |
| 261 | + 1..(size * 2) |
| 262 | + |> Enum.take_random(size) |
| 263 | + |> Enum.map(&%{id: &1, entry: %{other: "hey", foo: %{bar: "New#{&1 + 1}"}}}) |
| 264 | + end |
| 265 | +end |
0 commit comments