Skip to content

Commit 8832222

Browse files
authored
Add system test for SelectPanel re-anchoring on content size change
1 parent b4895a4 commit 8832222

1 file changed

Lines changed: 61 additions & 0 deletions

File tree

test/system/alpha/select_panel_test.rb

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,34 @@ def active_element
125125
page.evaluate_script("document.activeElement")
126126
end
127127

128+
def assert_anchored_above_invoker
129+
attempts = 0
130+
max_attempts = 3
131+
132+
begin
133+
attempts += 1
134+
135+
# Distance between the dialog's bottom edge and the invoker's top edge.
136+
# When properly anchored above the trigger this is the anchor offset
137+
# (a few pixels); when the dialog floats away it is much larger.
138+
gap = page.evaluate_script(<<~JS)
139+
(() => {
140+
const invoker = document.querySelector('select-panel button[aria-controls]')
141+
const dialog = document.querySelector('select-panel dialog')
142+
const invokerRect = invoker.getBoundingClientRect()
143+
const dialogRect = dialog.getBoundingClientRect()
144+
return Math.abs(invokerRect.top - dialogRect.bottom)
145+
})()
146+
JS
147+
148+
assert_operator gap, :<=, 16, "Expected dialog to remain anchored above its trigger, but it was #{gap}px away"
149+
rescue Minitest::Assertion => e
150+
raise e if attempts >= max_attempts
151+
sleep 1
152+
retry
153+
end
154+
end
155+
128156
########## TESTS ############
129157

130158
def test_invoker_opens_panel
@@ -225,6 +253,39 @@ def test_remembers_selections_on_filter
225253
assert_selector "[aria-selected=true]", count: 2
226254
end
227255

256+
def test_dialog_stays_anchored_to_invoker_when_content_size_changes
257+
visit_preview(:remote_fetch)
258+
259+
# Anchor the panel above its trigger and push the trigger down the page so
260+
# there is room above it. When anchored to outside-top the computed
261+
# position depends on the dialog's height, so a change in content size
262+
# must trigger a re-anchor.
263+
page.execute_script(<<~JS)
264+
const panel = document.querySelector('select-panel')
265+
panel.setAttribute('anchor-side', 'outside-top')
266+
panel.style.display = 'inline-block'
267+
panel.style.marginTop = '600px'
268+
JS
269+
270+
wait_for_items_to_load do
271+
click_on_invoker_button
272+
end
273+
274+
# Panel opens anchored above its trigger.
275+
assert_anchored_above_invoker
276+
277+
# Change the dialog's rendered size after it has been positioned. Without
278+
# re-anchoring on size changes, the dialog keeps its original top and
279+
# floats away from the trigger.
280+
page.execute_script(<<~JS)
281+
const dialog = document.querySelector('select-panel dialog')
282+
dialog.style.minHeight = '0'
283+
dialog.style.height = '150px'
284+
JS
285+
286+
assert_anchored_above_invoker
287+
end
288+
228289
def test_pressing_down_arrow_in_filter_input_focuses_first_item
229290
visit_preview(:default)
230291

0 commit comments

Comments
 (0)