@@ -81,4 +81,91 @@ def test_render_with_all_items
8181
8282 assert_match ( /Yes, RubyUI is pure Ruby and works great with Rails/ , output )
8383 end
84+
85+ # Regression test for issue #168:
86+ # Closed accordion content must not trap form validation errors in a
87+ # zero-height clipped region. Verify that:
88+ # - closed content has the `hidden` attribute (truly hidden from layout + focus)
89+ # - closed content carries data-state="closed" for CSS/semantic targeting
90+ # - open content does NOT have the `hidden` attribute
91+ # - open content carries data-state="open"
92+
93+ def test_closed_content_is_hidden
94+ output = phlex do
95+ RubyUI . Accordion do
96+ RubyUI . AccordionItem ( open : false ) do
97+ RubyUI . AccordionTrigger { "Trigger" }
98+ RubyUI . AccordionContent do
99+ "Hidden content"
100+ end
101+ end
102+ end
103+ end
104+
105+ # The content div must carry hidden so it is fully removed from layout,
106+ # preventing form field errors inside it from being invisible-but-focusable.
107+ assert_match ( /data-ruby-ui--accordion-target="content"[^>]*hidden/ , output )
108+ end
109+
110+ def test_closed_content_has_data_state_closed
111+ output = phlex do
112+ RubyUI . Accordion do
113+ RubyUI . AccordionItem ( open : false ) do
114+ RubyUI . AccordionTrigger { "Trigger" }
115+ RubyUI . AccordionContent do
116+ "Hidden content"
117+ end
118+ end
119+ end
120+ end
121+
122+ assert_match ( /data-state="closed"/ , output )
123+ end
124+
125+ def test_open_content_does_not_have_hidden
126+ output = phlex do
127+ RubyUI . Accordion do
128+ RubyUI . AccordionItem ( open : true ) do
129+ RubyUI . AccordionTrigger { "Trigger" }
130+ RubyUI . AccordionContent do
131+ "Visible content"
132+ end
133+ end
134+ end
135+ end
136+
137+ # When open: true the JS controller removes hidden on connect —
138+ # but at the Ruby/HTML level the content still starts with hidden.
139+ # The Stimulus controller handles removal at runtime. What we can assert
140+ # at the structural level is that the item is wired up with open: true
141+ # (Phlex renders boolean true as a bare attribute, no ="true").
142+ assert_match ( /data-ruby-ui--accordion-open-value/ , output )
143+ # Confirm the open: true value is set (bare attribute without ="false")
144+ refute_match ( /data-ruby-ui--accordion-open-value="false"/ , output )
145+ end
146+
147+ # Structural test: a FormField with a FormFieldError nested inside a closed
148+ # AccordionContent is present in the HTML (not stripped) but wrapped inside
149+ # an element that carries the `hidden` attribute, so the browser hides it
150+ # from layout and focus — the error cannot silently block form submission.
151+ def test_form_field_error_inside_closed_accordion_is_wrapped_in_hidden_element
152+ output = phlex do
153+ RubyUI . Accordion do
154+ RubyUI . AccordionItem ( open : false ) do
155+ RubyUI . AccordionTrigger { "Form section" }
156+ RubyUI . AccordionContent do |content |
157+ # Simulate a form validation error message inside a closed accordion
158+ content . span ( class : "text-destructive text-sm" ) { "This field is required" }
159+ end
160+ end
161+ end
162+ end
163+
164+ # Error text is in the DOM (server-rendered), but its ancestor content
165+ # container must carry `hidden` so the browser skips it for layout/focus.
166+ assert_match ( /This field is required/ , output )
167+ assert_match ( /hidden/ , output )
168+ # Confirm the hidden attribute belongs to the content target element
169+ assert_match ( /data-ruby-ui--accordion-target="content"[^>]*hidden/ , output )
170+ end
84171end
0 commit comments