Skip to content

Commit

Permalink
add toolbar component
Browse files Browse the repository at this point in the history
  • Loading branch information
woylie committed Jan 7, 2024
1 parent 9b85191 commit 2d855be
Show file tree
Hide file tree
Showing 4 changed files with 311 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
- New component: `Doggo.disclosure_button/1`.
- New component: `Doggo.radio_group/1`.
- New component: `Doggo.tabs/1`.
- New component: `Doggo.toolbar/1`.
- New component: `Doggo.tree/1`.
- Storybook page about modifier classes.
- Mix task `mix dog.modifiers` to list all modifier classes.
Expand Down
115 changes: 114 additions & 1 deletion lib/doggo.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4371,6 +4371,119 @@ defmodule Doggo do
"""
end

@doc """
Renders a container for a set of controls.
## Example
Direct children of this component can be any types buttons or groups of
buttons.
```heex
<Doggo.toolbar label="Actions for the dog">
<div role="group">
<button phx-click="feed-dog">
<Doggo.icon label="Feed dog"><Icons.feed /></Doggo.icon>
</button>
<button phx-click="walk-dog">
<Doggo.icon label="Walk dog"><Icons.walk /></Doggo.icon>
</button>
</div>
<div role="group">
<button phx-click="teach-trick">
<Doggo.icon label="Teach a Trick"><Icons.teach /></Doggo.icon>
</button>
<button phx-click="groom-dog">
<Doggo.icon label="Groom dog"><Icons.groom /></Doggo.icon>
</button>
</div>
</Doggo.toolbar>
```
"""
@doc type: :component

attr :label, :string,
default: nil,
doc: """
A accessibility label for the toolbar. Set as `aria-label` attribute.
You should ensure that either the `label` or the `labelledby` attribute is
set.
Do not repeat the word `toolbar` in the label, since it is already announced
by screen readers.
"""

attr :labelledby, :string,
default: nil,
doc: """
The DOM ID of an element that labels this tree.
Example:
```html
<h3 id="dog-toolbar-label">Dogs</h3>
<Doggo.toolbar labelledby="dog-toolbar-label"></Doggo.toolbar>
```
You should ensure that either the `label` or the `labelledby` attribute is
set.
"""

attr :controls, :string,
default: nil,
doc: """
DOM ID of the element that is controlled by this toolbar. For example,
if the toolbar provides text formatting options for an editable content
area, the values should be the ID of that content area.
"""

attr :rest, :global, doc: "Any additional HTML attributes."

slot :inner_block,
required: true,
doc: """
Place any number of buttons, groups of buttons, toggle buttons, menu
buttons, or disclosure buttons here.
"""

def toolbar(assigns) do
label = assigns[:label]
labelledby = assigns[:labelledby]

if (label && labelledby) || !(label || labelledby) do
raise """
invalid label attributes for toolbar
Doggo.toolbar requires either 'label' or 'labelledby' set for
accessibility, but not both.
## Examples
With label:
<Doggo.toolbar label="Actions for the dog"></Doggo.toolbar>
With labelledby:
<h3 id="dog-toolbar-label">Actions for the dog</h3>
<Doggo.toolbar labelledby="dog-toolbar-label"></Doggo.toolbar>
"""
end

~H"""
<div
role="toolbar"
aria-label={@label}
aria-labelledby={@labelledby}
aria-controls={@controls}
{@rest}
>
<%= render_slot(@inner_block) %>
</div>
"""
end

@doc """
Renders a hierarchical list as a tree.
Expand Down Expand Up @@ -4418,7 +4531,7 @@ defmodule Doggo do
attr :label, :string,
default: nil,
doc: """
A accessibility label for the truee. Set as `aria-label` attribute.
A accessibility label for the tree. Set as `aria-label` attribute.
You should ensure that either the `label` or the `labelledby` attribute is
set.
Expand Down
117 changes: 117 additions & 0 deletions priv/storybook/components/toolbar.story.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
defmodule Storybook.Components.Toolbar do
use PhoenixStorybook.Story, :component

def function, do: &Doggo.toolbar/1

def paw_svg do
"""
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="lucide lucide-paw-print"
>
<circle cx="11" cy="4" r="2" />
<circle cx="18" cy="8" r="2" />
<circle cx="20" cy="16" r="2" />
<path d="M9 10a5 5 0 0 1 5 5v3.5a3.5 3.5 0 0 1-6.84 1.045Q6.52 17.48 4.46 16.84A3.5 3.5 0 0 1 5.5 10Z" />
</svg>
"""
end

def pot_svg do
"""
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="lucide lucide-cooking-pot"
>
<path d="M2 12h20" /><path d="M20 12v8a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2v-8" /><path d="m4 8 16-4" /><path d="m8.86 6.78-.45-1.81a2 2 0 0 1 1.45-2.43l1.94-.48a2 2 0 0 1 2.43 1.46l.45 1.8" />
</svg>
"""
end

def teach_svg do
"""
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="lucide lucide-graduation-cap"
>
<path d="M22 10v6M2 10l10-5 10 5-10 5z" /><path d="M6 12v5c3 3 9 3 12 0v-5" />
</svg>
"""
end

def cut_svg do
"""
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="lucide lucide-scissors"
>
<circle cx="6" cy="6" r="3" /><path d="M8.12 8.12 12 12" /><path d="M20 4 8.12 15.88" /><circle
cx="6"
cy="18"
r="3"
/><path d="M14.8 14.8 20 20" />
</svg>
"""
end

def variations do
[
%Variation{
id: :default,
attributes: %{label: "Actions"},
slots: [
"""
<div role="group">
<button phx-click="feed-dog">
<Doggo.icon label="Feed dog">#{pot_svg()}</Doggo.icon>
</button>
<button phx-click="walk-dog">
<Doggo.icon label="Walk dog">#{paw_svg()}</Doggo.icon>
</button>
</div>
<div role="group">
<button phx-click="teach-trick">
<Doggo.icon label="Teach a Trick">#{teach_svg()}</Doggo.icon>
</button>
<button phx-click="groom-dog">
<Doggo.icon label="Groom dog">#{cut_svg()}</Doggo.icon>
</button>
</div>
"""
]
}
]
end
end
79 changes: 79 additions & 0 deletions test/doggo_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -5034,6 +5034,85 @@ defmodule DoggoTest do
end
end

describe "toolbar/1" do
test "default" do
assigns = %{}

html =
parse_heex(~H"""
<Doggo.toolbar label="Actions for dog">
buttons
</Doggo.toolbar>
""")

assert attribute(html, "div:root", "role") == "toolbar"
assert attribute(html, ":root", "aria-label") == "Actions for dog"
assert attribute(html, ":root", "aria-labelledby") == nil
assert text(html, ":root") == "buttons"
end

test "with labelledby" do
assigns = %{}

html =
parse_heex(~H"""
<Doggo.toolbar labelledby="toolbar-heading"></Doggo.toolbar>
""")

assert attribute(html, ":root", "aria-label") == nil
assert attribute(html, ":root", "aria-labelledby") == "toolbar-heading"
end

test "with controls" do
assigns = %{}

html =
parse_heex(~H"""
<Doggo.toolbar label="Actions for dog" controls="dog-panel"></Doggo.toolbar>
""")

assert attribute(html, ":root", "aria-controls") == "dog-panel"
end

test "raises if both label and labelledby are set" do
error =
assert_raise RuntimeError, fn ->
assigns = %{}

parse_heex(~H"""
<Doggo.toolbar label="Dog actions" labelledby="dog-toolbar-label">
</Doggo.toolbar>
""")
end

assert error.message =~ "invalid label attributes"
end

test "raises if neither label nor labelledby are set" do
error =
assert_raise RuntimeError, fn ->
assigns = %{}

parse_heex(~H"""
<Doggo.toolbar></Doggo.toolbar>
""")
end

assert error.message =~ "invalid label attributes"
end

test "with global attribute" do
assigns = %{}

html =
parse_heex(~H"""
<Doggo.toolbar label="Actions for dog" data-test="hello"></Doggo.toolbar>
""")

assert attribute(html, "div", "data-test") == "hello"
end
end

describe "tree/1" do
test "with label" do
assigns = %{}
Expand Down

0 comments on commit 2d855be

Please sign in to comment.