feat: contain mobile issue capture focus (Closes #1046)
This commit is contained in:
1 parent
c655cf5861
commit
be10dba4bd
4 files changed
+185
-2
No files matched your search
@@ -187,7 +187,17 @@ def test_release_artifact_files_one_mobile_issue_exactly_once_after_offline_relo
|
||||
|
||||
new_action.click()
|
||||
expect(page.locator("#create-issue-sheet")).to_have_class("create-issue-sheet open")
|
||||
assert page.evaluate("document.activeElement?.id") != "create-issue-title"
|
||||
assert page.evaluate("document.activeElement?.id") == "cancel-new-issue"
|
||||
assert page.evaluate("document.querySelector('main').inert") is True
|
||||
page.keyboard.press("Shift+Tab")
|
||||
assert page.evaluate("document.activeElement.closest('#create-issue-sheet')?.id") == "create-issue-sheet"
|
||||
page.keyboard.press("Escape")
|
||||
expect(page.locator("#create-issue-sheet")).not_to_have_class("create-issue-sheet open")
|
||||
assert page.evaluate("document.activeElement?.dataset.mobileTask") == "new"
|
||||
assert page.evaluate("document.querySelector('main').inert") is False
|
||||
new_action.click()
|
||||
expect(page.locator("#create-issue-sheet")).to_have_class("create-issue-sheet open")
|
||||
assert page.evaluate("document.activeElement?.id") == "cancel-new-issue"
|
||||
create_navigation = page.locator(".mobile-create-issue-nav")
|
||||
expect(create_navigation).to_be_visible()
|
||||
create_navigation_buttons = create_navigation.locator("button")
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
MODULE = Path(__file__).parents[1] / "frontend" / "create-issue-sheet.js"
|
||||
|
||||
|
||||
def run_node(script: str) -> dict:
|
||||
result = subprocess.run(
|
||||
["node", "-e", script],
|
||||
check=True,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
return json.loads(result.stdout)
|
||||
|
||||
|
||||
def test_issue_capture_modal_inerts_background_focuses_cancel_and_restores_exact_launcher():
|
||||
result = run_node(
|
||||
f"""
|
||||
const createIssueCapture = require({json.dumps(str(MODULE))});
|
||||
function element(id) {{
|
||||
return {{id, inert:false, hidden:false, disabled:false, isConnected:true,
|
||||
focus() {{ document.activeElement = this; }},
|
||||
getClientRects() {{ return [{{}}]; }} }};
|
||||
}}
|
||||
const launcher = element('mobile-new');
|
||||
const cancel = element('cancel-new-issue');
|
||||
const title = element('create-issue-title');
|
||||
const background = [element('header'), element('main'), element('mobile-task-dock')];
|
||||
const root = {{
|
||||
querySelector: selector => selector === '#cancel-new-issue' ? cancel : null,
|
||||
querySelectorAll: () => [cancel, title],
|
||||
addEventListener() {{}}, removeEventListener() {{}},
|
||||
}};
|
||||
const document = {{activeElement: launcher}};
|
||||
const lifecycle = createIssueCapture.createModalLifecycle({{
|
||||
root, background, document, requestClose() {{}},
|
||||
}});
|
||||
lifecycle.open(launcher);
|
||||
const opened = {{active:document.activeElement.id, inert:background.map(item => item.inert)}};
|
||||
lifecycle.close();
|
||||
process.stdout.write(JSON.stringify({{
|
||||
opened, closed:{{active:document.activeElement.id, inert:background.map(item => item.inert)}}
|
||||
}}));
|
||||
"""
|
||||
)
|
||||
|
||||
assert result == {
|
||||
"opened": {"active": "cancel-new-issue", "inert": [True, True, True]},
|
||||
"closed": {"active": "mobile-new", "inert": [False, False, False]},
|
||||
}
|
||||
|
||||
|
||||
def test_issue_capture_modal_wraps_visible_focus_and_escape_uses_close_request():
|
||||
result = run_node(
|
||||
f"""
|
||||
const createIssueCapture = require({json.dumps(str(MODULE))});
|
||||
function element(id, visible=true) {{
|
||||
return {{id, inert:false, hidden:!visible, disabled:false, isConnected:true,
|
||||
focus() {{ document.activeElement = this; }},
|
||||
getClientRects() {{ return visible ? [{{}}] : []; }} }};
|
||||
}}
|
||||
const launcher = element('empty-work-create');
|
||||
const cancel = element('cancel-new-issue');
|
||||
const hidden = element('hidden-control', false);
|
||||
const submit = element('submit-new-issue');
|
||||
const listeners = {{}};
|
||||
const root = {{
|
||||
querySelector: selector => selector === '#cancel-new-issue' ? cancel : null,
|
||||
querySelectorAll: () => [cancel, hidden, submit],
|
||||
addEventListener: (name, callback) => {{ listeners[name] = callback; }},
|
||||
removeEventListener() {{}},
|
||||
}};
|
||||
const document = {{activeElement: launcher}};
|
||||
let closes = 0;
|
||||
const lifecycle = createIssueCapture.createModalLifecycle({{
|
||||
root, background:[], document, requestClose() {{ closes += 1; }},
|
||||
}});
|
||||
lifecycle.open(launcher);
|
||||
document.activeElement = submit;
|
||||
let prevented = 0;
|
||||
listeners.keydown({{key:'Tab', shiftKey:false, preventDefault() {{ prevented += 1; }}}});
|
||||
const wrappedForward = document.activeElement.id;
|
||||
document.activeElement = cancel;
|
||||
listeners.keydown({{key:'Tab', shiftKey:true, preventDefault() {{ prevented += 1; }}}});
|
||||
const wrappedBackward = document.activeElement.id;
|
||||
listeners.keydown({{key:'Escape', shiftKey:false, preventDefault() {{ prevented += 1; }}}});
|
||||
process.stdout.write(JSON.stringify({{wrappedForward, wrappedBackward, closes, prevented}}));
|
||||
"""
|
||||
)
|
||||
|
||||
assert result == {
|
||||
"wrappedForward": "cancel-new-issue",
|
||||
"wrappedBackward": "submit-new-issue",
|
||||
"closes": 1,
|
||||
"prevented": 3,
|
||||
}
|
||||
Reference in new issue
Block a user