Repository: phoenixframework/phoenix
Pull request #5679 (2023-12-27 12:17:09)

Set Heroicon default size to native icon size
We recently added support for the new 'micro' variant with proper sizing (16x16 pixels).

This change updates the default size of the original icons (no suffix) and their 'solid' variant to be their native size (24x24 pixels).

The 'mini' variant remains at their native size (20x20 pixels).


Patch:
diff --git a/installer/lib/phx_new/generator.ex b/installer/lib/phx_new/generator.ex
index 1a44ffbe8..de8284e2e 100644
--- a/installer/lib/phx_new/generator.ex
+++ b/installer/lib/phx_new/generator.ex
@@ -37,8 +37,13 @@ defmodule Phx.New.Generator do
               @external_resource unquote(path)
               @file unquote(path)
               def render(unquote(name), unquote(source), var!(assigns))
-                  when is_list(var!(assigns)),
-                  do: unquote(compiled)
+                  when is_list(var!(assigns)) do
+                var!(maybe_heex_attr_gettext) = &unquote(__MODULE__).maybe_heex_attr_gettext/2
+                _ = var!(maybe_heex_attr_gettext)
+                var!(maybe_eex_gettext) = &unquote(__MODULE__).maybe_eex_gettext/2
+                _ = var!(maybe_eex_gettext)
+                unquote(compiled)
+              end
             end
           else
             quote do
@@ -434,4 +439,41 @@ defmodule Phx.New.Generator do
 
   defp random_string(length),
     do: :crypto.strong_rand_bytes(length) |> Base.encode64() |> binary_part(0, length)
+
+  # In the context of a HEEx attribute value, transforms a given message into a
+  # dynamic `gettext` call or a fixed-value string attribute, depending on the
+  # `gettext?` parameter.
+  #
+  # ## Examples
+  #
+  #     iex> ~s|<tag attr=#{maybe_heex_attr_gettext("Hello", true)} />|
+  #     ~S|<tag attr={gettext("Hello")} />|
+  #
+  #     iex> ~s|<tag attr=#{maybe_heex_attr_gettext("Hello", false)} />|
+  #     ~S|<tag attr="Hello" />|
+  def maybe_heex_attr_gettext(message, gettext?) do
+    if gettext? do
+      ~s|{gettext(#{inspect(message)})}|
+    else
+      inspect(message)
+    end
+  end
+
+  # In the context of an EEx template, transforms a given message into a dynamic
+  # `gettext` call or the message as is, depending on the `gettext?` parameter.
+  #
+  # ## Examples
+  #
+  #     iex> ~s|<tag>#{maybe_eex_gettext("Hello", true)}</tag>|
+  #     ~S|<tag><%= gettext("Hello") %></tag>|
+  #
+  #     iex> ~s|<tag>#{maybe_eex_gettext("Hello", false)}</tag>|
+  #     ~S|<tag>Hello</tag>|
+  def maybe_eex_gettext(message, gettext?) do
+    if gettext? do
+      ~s|<%= gettext(#{inspect(message)}) %>|
+    else
+      message
+    end
+  end
 end
diff --git a/installer/templates/phx_assets/tailwind.config.js b/installer/templates/phx_assets/tailwind.config.js
index 0fd26851c..743c7a794 100644
--- a/installer/templates/phx_assets/tailwind.config.js
+++ b/installer/templates/phx_assets/tailwind.config.js
@@ -51,7 +51,12 @@ module.exports = {
       matchComponents({
         "hero": ({name, fullPath}) => {
           let content = fs.readFileSync(fullPath).toString().replace(/\r?\n|\r/g, "")
-          let spacing = name.endsWith("-micro") ? theme("spacing.4") : theme("spacing.5")
+          let size = theme("spacing.6")
+          if (name.endsWith("-mini")) {
+            size = theme("spacing.5")
+          } else if (name.endsWith("-micro")) {
+            size = theme("spacing.4")
+          }
           return {
             [`--hero-${name}`]: `url('data:image/svg+xml;utf8,${content}')`,
             "-webkit-mask": `var(--hero-${name})`,
@@ -60,8 +65,8 @@ module.exports = {
             "background-color": "currentColor",
             "vertical-align": "middle",
             "display": "inline-block",
-            "width": spacing,
-            "height": spacing
+            "width": size,
+            "height": size
           }
         }
       }, {values})
diff --git a/installer/templates/phx_web/components/core_components.ex b/installer/templates/phx_web/components/core_components.ex
index 6d5841ea4..1a095dd8b 100644
--- a/installer/templates/phx_web/components/core_components.ex
+++ b/installer/templates/phx_web/components/core_components.ex
@@ -73,7 +73,7 @@ defmodule <%= @web_namespace %>.CoreComponents do
                   phx-click={JS.exec("data-cancel", to: "##{@id}")}
                   type="button"
                   class="-m-3 flex-none p-3 opacity-20 hover:opacity-40"
-                  aria-label=<%= if @gettext do %>{gettext("close")}<% else %>"close"<% end %>
+                  aria-label=<%= maybe_heex_attr_gettext.("close", @gettext) %>
                 >
                   <.icon name="hero-x-mark-solid" class="h-5 w-5" />
                 </button>
@@ -127,7 +127,7 @@ defmodule <%= @web_namespace %>.CoreComponents do
         <%%= @title %>
       </p>
       <p class="mt-2 text-sm leading-5"><%%= msg %></p>
-      <button type="button" class="group absolute top-1 right-1 p-2" aria-label=<%= if @gettext do %>{gettext("close")}<% else %>"close"<% end %>>
+      <button type="button" class="group absolute top-1 right-1 p-2" aria-label=<%= maybe_heex_attr_gettext.("close", @gettext) %>>
         <.icon name="hero-x-mark-solid" class="h-5 w-5 opacity-40 group-hover:opacity-70" />
       </button>
     </div>
@@ -147,28 +147,29 @@ defmodule <%= @web_namespace %>.CoreComponents do
   def flash_group(assigns) do
     ~H"""
     <div id={@id}>
-      <.flash kind={:info} title="Success!" flash={@flash} />
-      <.flash kind={:error} title="Error!" flash={@flash} />
+      <.flash kind={:info} title=<%= maybe_heex_attr_gettext.("Success!", @gettext) %> flash={@flash} />
+      <.flash kind={:error} title=<%= maybe_heex_attr_gettext.("Error!", @gettext) %> flash={@flash} />
       <.flash
         id="client-error"
         kind={:error}
-        title="We can't find the internet"
+        title=<%= maybe_heex_attr_gettext.("We can't find the internet", @gettext) %>
         phx-disconnected={show(".phx-client-error #client-error")}
         phx-connected={hide("#client-error")}
         hidden
       >
-        Attempting to reconnect <.icon name="hero-arrow-path" class="ml-1 h-3 w-3 animate-spin" />
+        <%= maybe_eex_gettext.("Attempting to reconnect", @gettext) %>
+        <.icon name="hero-arrow-path" class="ml-1 h-3 w-3 animate-spin" />
       </.flash>
 
       <.flash
         id="server-error"
         kind={:error}
-        title="Something went wrong!"
+        title=<%= maybe_heex_attr_gettext.("Something went wrong!", @gettext) %>
         phx-disconnected={show(".phx-server-error #server-error")}
         phx-connected={hide("#server-error")}
         hidden
       >
-        Hang in there while we get back on track
+        <%= maybe_eex_gettext.("Hang in there while we get back on track", @gettext) %>
         <.icon name="hero-arrow-path" class="ml-1 h-3 w-3 animate-spin" />
       </.flash>
     </div>
@@ -479,7 +480,7 @@ defmodule <%= @web_namespace %>.CoreComponents do
           <tr>
             <th :for={col <- @col} class="p-0 pb-4 pr-6 font-normal"><%%= col[:label] %></th>
             <th :if={@action != []} class="relative p-0 pb-4">
-              <span class="sr-only"><%= if @gettext do %><%%= gettext("Actions") %><% else %>Actions<% end %></span>
+              <span class="sr-only"><%= maybe_eex_gettext.("Actions", @gettext) %></span>
             </th>
           </tr>
         </thead>
diff --git a/lib/mix/phoenix.ex b/lib/mix/phoenix.ex
index 39d3b000a..7c6b8e6d5 100644
--- a/lib/mix/phoenix.ex
+++ b/lib/mix/phoenix.ex
@@ -29,6 +29,12 @@ defmodule Mix.Phoenix do
   def copy_from(apps, source_dir, binding, mapping) when is_list(mapping) do
     roots = Enum.map(apps, &to_app_source(&1, source_dir))
 
+    binding =
+      Keyword.merge(binding,
+        maybe_heex_attr_gettext: &maybe_heex_attr_gettext/2,
+        maybe_eex_gettext: &maybe_eex_gettext/2
+      )
+
     for {format, source_file_path, target} <- mapping do
       source =
         Enum.find_value(roots, fn root ->
@@ -359,4 +365,41 @@ defmodule Mix.Phoenix do
   def prepend_newline(string) do
     "\n" <> string
   end
+
+  # In the context of a HEEx attribute value, transforms a given message into a
+  # dynamic `gettext` call or a fixed-value string attribute, depending on the
+  # `gettext?` parameter.
+  #
+  # ## Examples
+  #
+  #     iex> ~s|<tag attr=#{maybe_heex_attr_gettext("Hello", true)} />|
+  #     ~S|<tag attr={gettext("Hello")} />|
+  #
+  #     iex> ~s|<tag attr=#{maybe_heex_attr_gettext("Hello", false)} />|
+  #     ~S|<tag attr="Hello" />|
+  defp maybe_heex_attr_gettext(message, gettext?) do
+    if gettext? do
+      ~s|{gettext(#{inspect(message)})}|
+    else
+      inspect(message)
+    end
+  end
+
+  # In the context of an EEx template, transforms a given message into a dynamic
+  # `gettext` call or the message as is, depending on the `gettext?` parameter.
+  #
+  # ## Examples
+  #
+  #     iex> ~s|<tag>#{maybe_eex_gettext("Hello", true)}</tag>|
+  #     ~S|<tag><%= gettext("Hello") %></tag>|
+  #
+  #     iex> ~s|<tag>#{maybe_eex_gettext("Hello", false)}</tag>|
+  #     ~S|<tag>Hello</tag>|
+  defp maybe_eex_gettext(message, gettext?) do
+    if gettext? do
+      ~s|<%= gettext(#{inspect(message)}) %>|
+    else
+      message
+    end
+  end
 end
diff --git a/priv/templates/phx.gen.live/core_components.ex b/priv/templates/phx.gen.live/core_components.ex
index 6d5841ea4..1a095dd8b 100644
--- a/priv/templates/phx.gen.live/core_components.ex
+++ b/priv/templates/phx.gen.live/core_components.ex
@@ -73,7 +73,7 @@ defmodule <%= @web_namespace %>.CoreComponents do
                   phx-click={JS.exec("data-cancel", to: "##{@id}")}
                   type="button"
                   class="-m-3 flex-none p-3 opacity-20 hover:opacity-40"
-                  aria-label=<%= if @gettext do %>{gettext("close")}<% else %>"close"<% end %>
+                  aria-label=<%= maybe_heex_attr_gettext.("close", @gettext) %>
                 >
                   <.icon name="hero-x-mark-solid" class="h-5 w-5" />
                 </button>
@@ -127,7 +127,7 @@ defmodule <%= @web_namespace %>.CoreComponents do
         <%%= @title %>
       </p>
       <p class="mt-2 text-sm leading-5"><%%= msg %></p>
-      <button type="button" class="group absolute top-1 right-1 p-2" aria-label=<%= if @gettext do %>{gettext("close")}<% else %>"close"<% end %>>
+      <button type="button" class="group absolute top-1 right-1 p-2" aria-label=<%= maybe_heex_attr_gettext.("close", @gettext) %>>
         <.icon name="hero-x-mark-solid" class="h-5 w-5 opacity-40 group-hover:opacity-70" />
       </button>
     </div>
@@ -147,28 +147,29 @@ defmodule <%= @web_namespace %>.CoreComponents do
   def flash_group(assigns) do
     ~H"""
     <div id={@id}>
-      <.flash kind={:info} title="Success!" flash={@flash} />
-      <.flash kind={:error} title="Error!" flash={@flash} />
+      <.flash kind={:info} title=<%= maybe_heex_attr_gettext.("Success!", @gettext) %> flash={@flash} />
+      <.flash kind={:error} title=<%= maybe_heex_attr_gettext.("Error!", @gettext) %> flash={@flash} />
       <.flash
         id="client-error"
         kind={:error}
-        title="We can't find the internet"
+        title=<%= maybe_heex_attr_gettext.("We can't find the internet", @gettext) %>
         phx-disconnected={show(".phx-client-error #client-error")}
         phx-connected={hide("#client-error")}
         hidden
       >
-        Attempting to reconnect <.icon name="hero-arrow-path" class="ml-1 h-3 w-3 animate-spin" />
+        <%= maybe_eex_gettext.("Attempting to reconnect", @gettext) %>
+        <.icon name="hero-arrow-path" class="ml-1 h-3 w-3 animate-spin" />
       </.flash>
 
       <.flash
         id="server-error"
         kind={:error}
-        title="Something went wrong!"
+        title=<%= maybe_heex_attr_gettext.("Something went wrong!", @gettext) %>
         phx-disconnected={show(".phx-server-error #server-error")}
         phx-connected={hide("#server-error")}
         hidden
       >
-        Hang in there while we get back on track
+        <%= maybe_eex_gettext.("Hang in there while we get back on track", @gettext) %>
         <.icon name="hero-arrow-path" class="ml-1 h-3 w-3 animate-spin" />
       </.flash>
     </div>
@@ -479,7 +480,7 @@ defmodule <%= @web_namespace %>.CoreComponents do
           <tr>
             <th :for={col <- @col} class="p-0 pb-4 pr-6 font-normal"><%%= col[:label] %></th>
             <th :if={@action != []} class="relative p-0 pb-4">
-              <span class="sr-only"><%= if @gettext do %><%%= gettext("Actions") %><% else %>Actions<% end %></span>
+              <span class="sr-only"><%= maybe_eex_gettext.("Actions", @gettext) %></span>
             </th>
           </tr>
         </thead>


Test patch:
diff --git a/installer/test/phx_new_test.exs b/installer/test/phx_new_test.exs
index ae1af8164..a09572c77 100644
--- a/installer/test/phx_new_test.exs
+++ b/installer/test/phx_new_test.exs
@@ -110,6 +110,8 @@ defmodule Mix.Tasks.Phx.NewTest do
 
       assert_file("phx_blog/lib/phx_blog_web/components/core_components.ex", fn file ->
         assert file =~ "defmodule PhxBlogWeb.CoreComponents"
+        assert file =~ ~S|aria-label={gettext("close")}|
+        assert file =~ ~S|<.flash kind={:info} title={gettext("Success!")} flash={@flash} />|
       end)
 
       assert_file("phx_blog/lib/phx_blog_web/components/layouts.ex", fn file ->
@@ -550,6 +552,17 @@ defmodule Mix.Tasks.Phx.NewTest do
     end)
   end
 
+  test "new with --no-gettext" do
+    in_tmp("new with no_gettext", fn ->
+      Mix.Tasks.Phx.New.run([@app_name, "--no-gettext"])
+
+      assert_file("phx_blog/lib/phx_blog_web/components/core_components.ex", fn file ->
+        assert file =~ ~S|aria-label="close"|
+        assert file =~ ~S|<.flash kind={:info} title="Success!" flash={@flash} />|
+      end)
+    end)
+  end
+
   test "new with binary_id" do
     in_tmp("new with binary_id", fn ->
       Mix.Tasks.Phx.New.run([@app_name, "--binary-id"])