From ab70e76b1ba7268f35f3ba470c7d4acda0c8d9ec Mon Sep 17 00:00:00 2001 From: Mathias Polligkeit Date: Mon, 1 Jan 2024 11:17:08 +0900 Subject: [PATCH 1/2] add warnings-as-errors flag on CI --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index df96a68f..8365b082 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -44,4 +44,4 @@ jobs: - name: Compile run: mix compile --warnings-as-errors - name: Run Tests - run: mix coveralls.github + run: mix coveralls.github --warnings-as-errors From d1dd1f2c0341592841ac28d91625b7e1ab3de3a1 Mon Sep 17 00:00:00 2001 From: Mathias Polligkeit Date: Mon, 1 Jan 2024 11:58:06 +0900 Subject: [PATCH 2/2] update docs --- CHANGELOG.md | 4 + lib/doggo.ex | 114 ++++++++++++++++++++++- priv/storybook/components/tabs.story.exs | 42 +++++++++ test/doggo_test.exs | 93 ++++++++++++++++++ 4 files changed, 250 insertions(+), 3 deletions(-) create mode 100644 priv/storybook/components/tabs.story.exs diff --git a/CHANGELOG.md b/CHANGELOG.md index 6dada08c..7a93ef37 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +### Added + +- New component: `Doggo.tabs/1`. + ## [0.4.0] - 2023-12-31 ### Added diff --git a/lib/doggo.ex b/lib/doggo.ex index dfc4c74c..068ccd34 100644 --- a/lib/doggo.ex +++ b/lib/doggo.ex @@ -2910,7 +2910,8 @@ defmodule Doggo do To toggle the modal visibility dynamically with the `open` attribute: 1. Omit the `open` attribute in the template. - 2. Use the `show_modal` and `hide_modal` functions to change the visibility. + 2. Use the `show_modal/1` and `hide_modal/1` functions to change the + visibility. #### Example @@ -2926,10 +2927,10 @@ defmodule Doggo do ``` - To open modal, use the `show_modal` function. + To open modal, use the `show_modal/1` function. ```heex - <.link phx-click={show_modal("pet-modal")}>show + <.link phx-click={Doggo.show_modal("pet-modal")}>show ``` ## CSS @@ -3665,6 +3666,10 @@ defmodule Doggo do @doc """ Renders navigation tabs. + This component is meant for tabs that link to a different view or live action. + If you want to render tabs that switch between in-page content panels, use + `tabs/1` instead. + ## Example ```heex @@ -3749,6 +3754,109 @@ defmodule Doggo do """ end + @doc """ + Renders tab panels. + + This component is meant for tabs that toggle content panels within the page. + If you want to link to a different view or live action, use + `tab_navigation/1` instead. + + > #### Keyboard interaction {: .warning} + > + > Keyboard interaction will be added in a future version. + + ## Example + + ```heex + + <:panel label="Golden Retriever"> +

+ Friendly, intelligent, great with families. Origin: Scotland. Needs + regular exercise. +

+ + <:panel label="Siberian Husky"> +

+ Energetic, outgoing, distinctive appearance. Origin: Northeast Asia. + Loves cold climates. +

+ + <:panel label="Dachshund"> +

+ Playful, stubborn, small size. Origin: Germany. Enjoys sniffing games. +

+ +
+ ``` + """ + @doc type: :component + + attr :id, :string, required: true + attr :title, :string, required: true, doc: "A title that labels the tabs." + + 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 :panel, required: true do + attr :label, :string + end + + def tabs(assigns) do + ~H""" +
+

<%= @title %>

+
+ +
+ +
+ """ + end + + @doc """ + Shows the tab with the given index of the `tabs/1` component with the given + ID. + + ## Example + + Doggo.show_tab("my-tabs", 2) + """ + def show_tab(js \\ %JS{}, id, index) + when is_binary(id) and is_integer(index) do + other_tabs = "##{id} [role='tab']:not(##{id}-tab-#{index})" + other_panels = "##{id} [role='tabpanel']:not(##{id}-panel-#{index})" + + js + |> JS.set_attribute({"aria-selected", "true"}, to: "##{id}-tab-#{index}") + |> JS.set_attribute({"tabindex", "0"}, to: "##{id}-tab-#{index}") + |> JS.remove_attribute("hidden", to: "##{id}-panel-#{index}") + |> JS.set_attribute({"aria-selected", "false"}, to: other_tabs) + |> JS.set_attribute({"tabindex", "-1"}, to: other_tabs) + |> JS.set_attribute({"hidden", "hidden"}, to: other_panels) + end + @doc """ Renders a button that toggles a state. diff --git a/priv/storybook/components/tabs.story.exs b/priv/storybook/components/tabs.story.exs new file mode 100644 index 00000000..575edc67 --- /dev/null +++ b/priv/storybook/components/tabs.story.exs @@ -0,0 +1,42 @@ +defmodule Storybook.Components.Tabs do + use PhoenixStorybook.Story, :component + + def function, do: &Doggo.tabs/1 + + def variations do + [ + %Variation{ + id: :default, + attributes: %{ + id: "dog-breed-profiles", + title: "Dog Breed Profiles" + }, + slots: [ + """ + <:panel label="Golden Retriever"> +

+ Friendly, intelligent, great with families. Origin: Scotland. Needs + regular exercise. +

+ + """, + """ + <:panel label="Siberian Husky"> +

+ Energetic, outgoing, distinctive appearance. Origin: Northeast Asia. + Loves cold climates. +

+ + """, + """ + <:panel label="Dachshund"> +

+ Playful, stubborn, small size. Origin: Germany. Enjoys sniffing games. +

+ + """ + ] + } + ] + end +end diff --git a/test/doggo_test.exs b/test/doggo_test.exs index cf29826f..742017d8 100644 --- a/test/doggo_test.exs +++ b/test/doggo_test.exs @@ -3983,6 +3983,99 @@ defmodule DoggoTest do end end + describe "tabs/1" do + test "default" do + assigns = %{} + + html = + parse_heex(~H""" + + <:panel label="Panel 1">some text + <:panel label="Panel 2">some other text + + """) + + div = find_one(html, "div:root") + assert attribute(div, "class") == "tabs" + assert attribute(div, "id") == "my-tabs" + + assert attribute(html, ":root > h3", "id") == "my-tabs-title" + assert text(html, ":root > h3") == "My Tabs" + + div = find_one(html, ":root > div[role='tablist']") + assert attribute(div, "aria-labelledby") == "my-tabs-title" + + button = find_one(div, "button:first-child") + assert attribute(button, "type") == "button" + assert attribute(button, "role") == "tab" + assert attribute(button, "id") == "my-tabs-tab-1" + assert attribute(button, "aria-selected") == "true" + assert attribute(button, "aria-controls") == "my-tabs-panel-1" + assert attribute(button, "tabindex") == nil + assert text(button) == "Panel 1" + + button = find_one(div, "button:last-child") + assert attribute(button, "type") == "button" + assert attribute(button, "role") == "tab" + assert attribute(button, "id") == "my-tabs-tab-2" + assert attribute(button, "aria-selected") == "false" + assert attribute(button, "aria-controls") == "my-tabs-panel-2" + assert attribute(button, "tabindex") == "-1" + assert text(button) == "Panel 2" + + div = find_one(html, ":root > div#my-tabs-panel-1") + assert attribute(div, "role") == "tabpanel" + assert attribute(div, "aria-labelledby") == "my-tabs-tab-1" + assert attribute(div, "hidden") == nil + assert text(div) == "some text" + + div = find_one(html, ":root > div#my-tabs-panel-2") + assert attribute(div, "role") == "tabpanel" + assert attribute(div, "aria-labelledby") == "my-tabs-tab-2" + assert attribute(div, "hidden") == "hidden" + assert text(div) == "some other text" + end + + test "with additional class as string" do + assigns = %{} + + html = + parse_heex(~H""" + + <:panel label="Panel 1">some text + + """) + + assert attribute(html, ":root", "class") == "tabs is-rad" + end + + test "with additional classes as list" do + assigns = %{} + + html = + parse_heex(~H""" + + <:panel label="Panel 1">some text + + """) + + assert attribute(html, ":root", "class") == "tabs is-rad is-dark" + end + + test "with global attribute" do + assigns = %{} + + html = + parse_heex(~H""" + + <:panel label="Panel 1">some text + + """) + + assert attribute(html, ":root", "data-test") == "hello" + end + end + describe "table/1" do test "default" do assigns = %{pets: [%{id: 1, name: "George"}]}