Repository: woylie/doggo
Pull request #228 (2024-02-11 13:06:50)

add menu group component
contributes to #124 


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 89fa91a..e4e84a9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,7 @@
 - New component: `Doggo.menu/1`.
 - New component: `Doggo.menu_bar/1`.
 - New component: `Doggo.menu_button/1`.
+- New component: `Doggo.menu_group/1`.
 - New component: `Doggo.menu_item/1`.
 - New component: `Doggo.radio_group/1`.
 - New component: `Doggo.split_pane/1`.
diff --git a/lib/doggo.ex b/lib/doggo.ex
index e2c5cc6..b788281 100644
--- a/lib/doggo.ex
+++ b/lib/doggo.ex
@@ -3430,7 +3430,7 @@ defmodule Doggo do
   This component is meant for organizing actions within an application, rather
   than for navigating between different pages or sections of a website.
 
-  See also `menu_bar/1` and `menu_button/1`.
+  See also `menu_bar/1`, `menu_button/1`, and `menu_group/1`.
 
   > #### In Development {: .warning}
   >
@@ -3540,7 +3540,7 @@ defmodule Doggo do
   This component is meant for organizing actions within an application, rather
   than for navigating between different pages or sections of a website.
 
-  See also `menu/1`, `menu_button/1`, and `menu_item/1`.
+  See also `menu/1`, `menu_button/1`, `menu_item/1`, and `menu_group/1`.
 
   > #### In Development {: .warning}
   >
@@ -3657,7 +3657,7 @@ defmodule Doggo do
   Renders a button that toggles an actions menu.
 
   This component can be used on its own or as part of a `menu_bar/1` or `menu/1`.
-  See also `menu_item/1`.
+  See also `menu_item/1` and `menu_group/1`.
 
   For a button that toggles the visibility of an element that is not a menu, use
   `disclosure_button/1`. For a button that toggles other states, use
@@ -3759,6 +3759,89 @@ defmodule Doggo do
     """
   end
 
+  @doc """
+  This component can be used to group items within a `menu/1` or `menu_bar/1`.
+
+  See also `menu_button/1` and  `menu_item/1`.
+
+  > #### In Development {: .warning}
+  >
+  > The necessary JavaScript for making this component fully functional and
+  > accessible will be added in a future version.
+  >
+  > **Missing features**
+  >
+  > - Focus management
+  > - Keyboard support
+
+  ## Example
+
+  ```heex
+  <Doggo.menu id="actions-menu" labelledby="actions-button" hidden>
+    <:item>
+      <Doggo.menu_group label="Dog actions">
+        <:item>
+          <Doggo.menu_item on_click={JS.push("view-dog-profiles")}>
+            View Dog Profiles
+          </Doggo.menu_item>
+        </:item>
+        <:item>
+          <Doggo.menu_item on_click={JS.push("add-dog-profile")}>
+            Add Dog Profile
+          </Doggo.menu_item>
+        </:item>
+        <:item>
+          <Doggo.menu_item on_click={JS.push("dog-care-tips")}>
+            Dog Care Tips
+          </Doggo.menu_item>
+        </:item>
+      </Doggo.menu_group>
+    </:item>
+    <:item role="separator" />
+    <:item>
+      <Doggo.menu_item on_click={JS.push("help")}>Help</Doggo.menu_item>
+    </:item>
+  </Doggo.menu>
+  ```
+  """
+  @doc type: :component
+  @doc since: "0.5.0"
+
+  attr :label, :string,
+    required: true,
+    doc: """
+    A accessibility label for the group. Set as `aria-label` attribute.
+    """
+
+  attr :class, :any,
+    default: [],
+    doc: "Additional CSS classes. Can be a string or a list of strings."
+
+  attr :rest, :global, doc: "Any additional HTML attributes."
+
+  slot :item, required: true do
+    attr :role, :string,
+      values: ["none", "separator"],
+      doc: """
+      Sets the role of the list item. If the item has a menu item, menu
+      item radio group or menu item checkbox as a child, use `"none"`. If you
+      want to render a visual separator, use `"separator"`. The default is
+      `"none"`.
+      """
+  end
+
+  def menu_group(assigns) do
+    ~H"""
+    <ul class={@class} role="group" aria-label={@label} {@rest}>
+      <li :for={item <- @item} role={Map.get(item, :role, "none")}>
+        <%= if item[:role] != "separator" do %>
+          <%= render_slot(item) %>
+        <% end %>
+      </li>
+    </ul>
+    """
+  end
+
   @doc """
   Renders a button that acts as a menu item within a `menu/1` or `menu_bar/1`.
 


Test patch:
diff --git a/test/doggo_test.exs b/test/doggo_test.exs
index 6236704..d529e70 100644
--- a/test/doggo_test.exs
+++ b/test/doggo_test.exs
@@ -3949,6 +3949,81 @@ defmodule DoggoTest do
     end
   end
 
+  describe "menu_group/1" do
+    test "default" do
+      assigns = %{}
+
+      html =
+        parse_heex(~H"""
+        <Doggo.menu_group label="Dog actions">
+          <:item>A</:item>
+        </Doggo.menu_group>
+        """)
+
+      ul = find_one(html, "ul:root")
+      assert attribute(ul, "role") == "group"
+      assert attribute(ul, "aria-label") == "Dog actions"
+
+      assert li = find_one(html, "ul > li")
+      assert attribute(li, "role") == "none"
+      assert text(li) == "A"
+    end
+
+    test "with separator" do
+      assigns = %{}
+
+      html =
+        parse_heex(~H"""
+        <Doggo.menu_group label="Dog actions">
+          <:item role="separator">A</:item>
+        </Doggo.menu_group>
+        """)
+
+      assert li = find_one(html, "ul > li")
+      assert attribute(li, "role") == "separator"
+      assert text(li) == ""
+    end
+
+    test "with additional class as string" do
+      assigns = %{}
+
+      html =
+        parse_heex(~H"""
+        <Doggo.menu_group label="Dog actions" class="is-rad">
+          <:item>A</:item>
+        </Doggo.menu_group>
+        """)
+
+      assert attribute(html, ":root", "class") == "is-rad"
+    end
+
+    test "with additional classes as list" do
+      assigns = %{}
+
+      html =
+        parse_heex(~H"""
+        <Doggo.menu_group label="Dog Carousel" class={["is-rad", "is-good"]}>
+          <:item>A</:item>
+        </Doggo.menu_group>
+        """)
+
+      assert attribute(html, ":root", "class") == "is-rad is-good"
+    end
+
+    test "with global attribute" do
+      assigns = %{}
+
+      html =
+        parse_heex(~H"""
+        <Doggo.menu_group label="Dog actions" data-test="hello">
+          <:item>A</:item>
+        </Doggo.menu_group>
+        """)
+
+      assert attribute(html, ":root", "data-test") == "hello"
+    end
+  end
+
   describe "menu_item/1" do
     test "default" do
       assigns = %{}