Skip to content

Commit

Permalink
[#16] Add component: modal (#105)
Browse files Browse the repository at this point in the history
* [#16] Add component: modal

#16

* Update lib/bitstyles_phoenix/component/modal.ex
  • Loading branch information
andreasknoepfle authored May 15, 2023
1 parent 45a911f commit 83f7d2c
Show file tree
Hide file tree
Showing 5 changed files with 191 additions and 5 deletions.
4 changes: 2 additions & 2 deletions .tool-versions
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
elixir 1.13.4
erlang 24.3.4.2
elixir 1.14.1
erlang 25.1.2
nodejs 16.13.0
1 change: 1 addition & 0 deletions lib/bitstyles_phoenix.ex
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ defmodule BitstylesPhoenix do
import BitstylesPhoenix.Component.Form
import BitstylesPhoenix.Component.Heading
import BitstylesPhoenix.Component.Icon
import BitstylesPhoenix.Component.Modal
import BitstylesPhoenix.Component.Sidebar
import BitstylesPhoenix.Component.Tabs
import BitstylesPhoenix.Component.UseSVG
Expand Down
181 changes: 181 additions & 0 deletions lib/bitstyles_phoenix/component/modal.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
defmodule BitstylesPhoenix.Component.Modal do
use BitstylesPhoenix.Component

import BitstylesPhoenix.Component.Content

@moduledoc """
The Modal component.
"""

@doc ~s"""
Renders a modal. Optionally can have an `overlay` slot with or without content, to change the overlay div.
Additionally the modal can implement a `content` slot to change the content wrapper.
If a content slot is passed, then the content of that slot is rendered as inner content of the modal, otherwise
as a shortcut, the inner_block of the component is used.
## Attributes
- `class` - Extra classes to pass to the modal. See `BitstylesPhoenix.Helper.classnames/1` for usage.
- All other attributes are passed to the underlying `ui_content/1` component.
# Attributes - `overlay` slot
- `class` - Extra classes to pass to the `main` tag.
See `BitstylesPhoenix.Helper.classnames/1` for usage.
- All other attributes are passed on to the main tag.
# Attributes - `content` slot
- `class` - Extra classes to pass to the `main` tag.
See `BitstylesPhoenix.Helper.classnames/1` for usage.
- All other attributes are passed on to the main tag.
See [bitstyles content docs](https://bitcrowd.github.io/bitstyles/?path=/docs/ui-modal--informational-modal) for examples, and for the default variants available.
"""

story(
"Default modal",
'''
iex> assigns = %{}
...> render ~H"""
...> <.ui_modal>
...> Content
...> </.ui_modal>
...> """
"""
<div class="o-modal" aria-modal="true" role="dialog">
<div class="o-modal__overlay">
</div>
<div class="a-content o-modal__content a-card u-padding-xl@m u-padding-l u-flex u-flex-col" role="document">
Content
</div>
</div>
"""
''',
height: "400px",
width: "100%",
transparent: false
)

story(
"Small modal",
'''
iex> assigns = %{}
...> render ~H"""
...> <.ui_modal aria-labelledby="dialog-title" variant="s" class="foo">
...> <:content class="bar" foo="bar">
...> Content
...> </:content>
...> </.ui_modal>
...> """
"""
<div class="o-modal foo" aria-modal="true" role="dialog" aria-labelledby="dialog-title">
<div class="o-modal__overlay">
</div>
<div class="a-content a-content--s o-modal__content a-card u-padding-xl@m u-padding-l u-flex u-flex-col bar" foo="bar" role="document">
Content
</div>
</div>
"""
''',
height: "400px",
width: "100%",
transparent: false
)

story(
"Small modal with extra overlay",
'''
iex> assigns = %{}
...> render ~H"""
...> <.ui_modal>
...> <:overlay class="extra" data-foo="bar" />
...> Content
...> </.ui_modal>
...> """
"""
<div class="o-modal" aria-modal="true" role="dialog">
<div class="o-modal__overlay extra" data-foo="bar">
</div>
<div class="a-content o-modal__content a-card u-padding-xl@m u-padding-l u-flex u-flex-col" role="document">
Content
</div>
</div>
"""
''',
height: "400px",
width: "100%",
transparent: false
)

story(
"Small modal with extra overlay content",
'''
iex> assigns = %{}
...> render ~H"""
...> <.ui_modal>
...> <:overlay class="extra" data-foo="bar">
...> <span class="u-hidden">Overlay content</span>
...> </:overlay>
...> Content
...> </.ui_modal>
...> """
"""
<div class="o-modal" aria-modal="true" role="dialog">
<div class="o-modal__overlay extra" data-foo="bar">
<span class="u-hidden">
Overlay content
</span>
</div>
<div class="a-content o-modal__content a-card u-padding-xl@m u-padding-l u-flex u-flex-col" role="document">
Content
</div>
</div>
"""
''',
height: "400px",
width: "100%",
transparent: false
)

def ui_modal(assigns) do
{overlay, overlay_extra} = assigns_from_single_slot(assigns, :overlay, optional: true)
overlay_class = classnames(["o-modal__overlay", overlay_extra[:class]])
overlay_extra = Keyword.put(overlay_extra, :class, overlay_class)

{content, content_extra} = assigns_from_single_slot(assigns, :content, optional: true)

content_class =
classnames([
"o-modal__content a-card u-padding-xl@m u-padding-l u-flex u-flex-col",
content_extra[:class]
])

content_extra = Keyword.merge(content_extra, class: content_class, variant: assigns[:variant])

assigns =
assign(assigns,
overlay: overlay,
overlay_extra: overlay_extra,
content: content,
content_extra: content_extra
)

~H"""
<div
class={classnames(["o-modal", assigns[:class]])}
aria-modal="true"
role="dialog"
{assigns_to_attributes(assigns, [:overlay, :overlay_extra, :class, :content, :content_extra, :variant])}
>
<div {@overlay_extra}>
<%= if assigns[:overlay] && Map.get(assigns[:overlay], :inner_block), do: render_slot(@overlay) %>
</div>
<.ui_content role="document" {@content_extra}>
<%= if assigns[:content], do: render_slot(@content), else: render_slot(@inner_block) %>
</.ui_content>
</div>
"""
end
end
5 changes: 2 additions & 3 deletions mix.lock
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,9 @@
"phoenix_live_view": {:hex, :phoenix_live_view, "0.18.18", "1f38fbd7c363723f19aad1a04b5490ff3a178e37daaf6999594d5f34796c47fc", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.15 or ~> 1.7.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.3", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "a5810d0472f3189ede6d2a95bda7f31c6113156b91784a3426cb0ab6a6d85214"},
"phoenix_pubsub": {:hex, :phoenix_pubsub, "2.1.1", "ba04e489ef03763bf28a17eb2eaddc2c20c6d217e2150a61e3298b0f4c2012b5", [:mix], [], "hexpm", "81367c6d1eea5878ad726be80808eb5a787a23dee699f96e72b1109c57cdd8d9"},
"phoenix_template": {:hex, :phoenix_template, "1.0.1", "85f79e3ad1b0180abb43f9725973e3b8c2c3354a87245f91431eec60553ed3ef", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "157dc078f6226334c91cb32c1865bf3911686f8bcd6bcff86736f6253e6993ee"},
"phoenix_view": {:hex, :phoenix_view, "1.1.2", "1b82764a065fb41051637872c7bd07ed2fdb6f5c3bd89684d4dca6e10115c95a", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "7ae90ad27b09091266f6adbb61e1d2516a7c3d7062c6789d46a7554ec40f3a56"},
"plug": {:hex, :plug, "1.14.1", "3148623796853ae96c628960b833bf6b6a894d6bdc8c199ef7160c41149b71f2", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "a0e789be21a576b11ec55a0983e4e8f7c7b07d88dfb3b8da9e97767132271d40"},
"plug": {:hex, :plug, "1.14.2", "cff7d4ec45b4ae176a227acd94a7ab536d9b37b942c8e8fa6dfc0fff98ff4d80", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "842fc50187e13cf4ac3b253d47d9474ed6c296a8732752835ce4a86acdf68d13"},
"plug_crypto": {:hex, :plug_crypto, "1.2.5", "918772575e48e81e455818229bf719d4ab4181fcbf7f85b68a35620f78d89ced", [:mix], [], "hexpm", "26549a1d6345e2172eb1c233866756ae44a9609bd33ee6f99147ab3fd87fd842"},
"telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"},
"websock": {:hex, :websock, "0.5.0", "f6bbce90226121d62a0715bca7c986c5e43de0ccc9475d79c55381d1796368cc", [:mix], [], "hexpm", "b51ac706df8a7a48a2c622ee02d09d68be8c40418698ffa909d73ae207eb5fb8"},
"websock": {:hex, :websock, "0.5.1", "c496036ce95bc26d08ba086b2a827b212c67e7cabaa1c06473cd26b40ed8cf10", [:mix], [], "hexpm", "b9f785108b81cd457b06e5f5dabe5f65453d86a99118b2c0a515e1e296dc2d2c"},
"websock_adapter": {:hex, :websock_adapter, "0.5.0", "cea35d8bbf1a6964e32d4b02ceb561dfb769c04f16d60d743885587e7d2ca55b", [:mix], [{:bandit, "~> 0.6", [hex: :bandit, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "16318b124effab8209b1eb7906c636374f623dc9511a8278ad09c083cea5bb83"},
}
5 changes: 5 additions & 0 deletions test/bitstyles_phoenix/component/modal_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
defmodule BitstylesPhoenix.Component.ModalTest do
use BitstylesPhoenix.ComponentCase, async: true

doctest BitstylesPhoenix.Component.Modal
end

0 comments on commit 83f7d2c

Please sign in to comment.