Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 7 additions & 19 deletions gem/lib/ruby_ui/dialog/dialog_content.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,24 +17,20 @@ def initialize(size: :md, **attrs)
end

def view_template
template(data: {ruby_ui__dialog_target: "content"}) do
div(data_controller: "ruby-ui--dialog") do
backdrop
div(**attrs) do
yield
close_button
end
end
dialog(**attrs) do
yield
close_button
end
end

private

def default_attrs
{
data_state: "open",
data_ruby_ui__dialog_target: "dialog",
data_action: "click->ruby-ui--dialog#backdropClick",
class: [
"fixed flex flex-col pointer-events-auto left-[50%] top-[50%] z-50 w-full max-h-screen overflow-y-auto translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 sm:rounded-lg md:w-full",
"fixed flex flex-col pointer-events-auto left-[50%] top-[50%] z-50 w-full max-h-screen overflow-y-auto translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 backdrop:bg-background/80 backdrop:backdrop-blur-sm open:animate-in open:fade-in-0 open:zoom-in-95 sm:rounded-lg md:w-full",
SIZES[@size]
]
}
Expand All @@ -43,7 +39,7 @@ def default_attrs
def close_button
button(
type: "button",
class: "absolute end-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground",
class: "absolute end-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none",
data_action: "click->ruby-ui--dialog#dismiss"
) do
svg(
Expand All @@ -65,13 +61,5 @@ def close_button
span(class: "sr-only") { "Close" }
end
end

def backdrop
div(
data_state: "open",
data_action: "click->ruby-ui--dialog#dismiss esc->ruby-ui--dialog#dismiss",
class: "fixed pointer-events-auto inset-0 z-50 bg-background/80 backdrop-blur-sm data-[state=open]:animate-in data-[state=open]:fade-in-0"
)
end
end
end
31 changes: 21 additions & 10 deletions gem/lib/ruby_ui/dialog/dialog_controller.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Controller } from "@hotwired/stimulus"

// Connects to data-controller="dialog"
// Connects to data-controller="ruby-ui--dialog"
export default class extends Controller {
static targets = ["content"]
static targets = ["dialog"]
static values = {
open: {
type: Boolean,
Expand All @@ -11,22 +11,33 @@ export default class extends Controller {
}

connect() {
this.dialogTarget.addEventListener("close", this.handleClose)
if (this.openValue) {
this.open()
}
}

disconnect() {
this.dialogTarget.removeEventListener("close", this.handleClose)
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
}

open(e) {
e?.preventDefault();
document.body.insertAdjacentHTML('beforeend', this.contentTarget.innerHTML)
// prevent scroll on body
document.body.classList.add('overflow-hidden')
e?.preventDefault()
this.dialogTarget.showModal()
document.body.classList.add("overflow-hidden")
}

dismiss() {
// allow scroll on body
document.body.classList.remove('overflow-hidden')
// remove the element
this.element.remove()
this.dialogTarget.close()
}

backdropClick(e) {
if (e.target === this.dialogTarget) {
this.dismiss()
}
}

handleClose = () => {
document.body.classList.remove("overflow-hidden")
}
}
88 changes: 88 additions & 0 deletions gem/test/ruby_ui/dialog_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,92 @@ def test_render_with_all_items

assert_match(/Open Dialog/, output)
end

# Regression test for #343: Dialog content must use native <dialog> element, not <div>
def test_dialog_content_renders_native_dialog_element
output = phlex do
RubyUI.Dialog do
RubyUI.DialogContent { "Content" }
end
end

assert_match(/<dialog[\s>]/, output, "DialogContent must render a native <dialog> element")
refute_match(/<template[\s>]/, output, "DialogContent must not use a <template> element")
end

def test_dialog_wrapper_renders_as_div_with_stimulus_controller
output = phlex do
RubyUI.Dialog do
RubyUI.DialogContent { "Content" }
end
end

assert_match(/data-controller="ruby-ui--dialog"/, output)
assert_match(/<div[^>]*data-controller="ruby-ui--dialog"/, output, "Dialog wrapper must be a <div>")
end

def test_dialog_content_has_stimulus_target
output = phlex do
RubyUI.Dialog do
RubyUI.DialogContent { "Content" }
end
end

assert_match(/data-ruby-ui--dialog-target="dialog"/, output)
end

def test_dialog_content_has_backdrop_click_action
output = phlex do
RubyUI.Dialog do
RubyUI.DialogContent { "Content" }
end
end

assert_match(/data-action="click->ruby-ui--dialog#backdropClick"/, output)
end

def test_dialog_content_sizes
{xs: "max-w-sm", sm: "max-w-md", md: "max-w-lg", lg: "max-w-2xl", xl: "max-w-4xl", full: "max-w-full"}.each do |size, expected_class|
output = phlex do
RubyUI.Dialog do
RubyUI.DialogContent(size: size) { "Content" }
end
end

assert_match(/#{Regexp.escape(expected_class)}/, output, "Size #{size} should apply class #{expected_class}")
end
end

def test_dialog_open_value_is_set_on_wrapper
output = phlex do
RubyUI.Dialog(open: true) do
RubyUI.DialogContent { "Content" }
end
end

assert_match(/data-ruby-ui--dialog-open-value/, output)
end

def test_close_button_has_dismiss_action
output = phlex do
RubyUI.Dialog do
RubyUI.DialogContent { "Content" }
end
end

assert_match(/data-action="click->ruby-ui--dialog#dismiss"/, output)
end

def test_trigger_has_open_action
output = phlex do
RubyUI.Dialog do
RubyUI.DialogTrigger do
RubyUI.Button { "Open" }
end
RubyUI.DialogContent { "Content" }
end
end

assert_match(/data-action="click->ruby-ui--dialog#open"/, output)
end
end
Loading