зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1428078 - Enable keyboard navigation in the object sidebar; r=bgrins.
MozReview-Commit-ID: JuZOX3e9AXy --HG-- extra : rebase_source : d3213b0797d8ca2ed6c5f4b5f42b606b323edf92
This commit is contained in:
Родитель
7f1f1892fb
Коммит
295ace1eb2
|
@ -59,6 +59,10 @@ function GripMessageBody(props) {
|
||||||
let objectInspectorProps = {
|
let objectInspectorProps = {
|
||||||
autoExpandDepth: shouldAutoExpandObjectInspector(props) ? 1 : 0,
|
autoExpandDepth: shouldAutoExpandObjectInspector(props) ? 1 : 0,
|
||||||
mode,
|
mode,
|
||||||
|
// TODO: we disable focus since the tabbing trail is a bit weird in the output (e.g.
|
||||||
|
// location links are not focused). Let's remove the property below when we found and
|
||||||
|
// fixed the issue (See Bug 1456060).
|
||||||
|
focusable: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (typeof grip === "string" || (grip && grip.type === "longString")) {
|
if (typeof grip === "string" || (grip && grip.type === "longString")) {
|
||||||
|
|
|
@ -29,13 +29,26 @@ class SideBar extends Component {
|
||||||
this.onClickSidebarClose = this.onClickSidebarClose.bind(this);
|
this.onClickSidebarClose = this.onClickSidebarClose.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
shouldComponentUpdate(nextProps) {
|
||||||
|
const {
|
||||||
|
grip,
|
||||||
|
sidebarVisible,
|
||||||
|
} = nextProps;
|
||||||
|
|
||||||
|
return sidebarVisible !== this.props.sidebarVisible
|
||||||
|
|| grip !== this.props.grip;
|
||||||
|
}
|
||||||
|
|
||||||
onClickSidebarClose() {
|
onClickSidebarClose() {
|
||||||
this.props.dispatch(actions.sidebarClose());
|
this.props.dispatch(actions.sidebarClose());
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
if (!this.props.sidebarVisible) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
let {
|
let {
|
||||||
sidebarVisible,
|
|
||||||
grip,
|
grip,
|
||||||
serviceContainer,
|
serviceContainer,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
@ -43,6 +56,7 @@ class SideBar extends Component {
|
||||||
let objectInspector = getObjectInspector(grip, serviceContainer, {
|
let objectInspector = getObjectInspector(grip, serviceContainer, {
|
||||||
autoExpandDepth: 1,
|
autoExpandDepth: 1,
|
||||||
mode: MODE.SHORT,
|
mode: MODE.SHORT,
|
||||||
|
autoFocusRoot: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
let endPanel = dom.aside({
|
let endPanel = dom.aside({
|
||||||
|
@ -61,18 +75,14 @@ class SideBar extends Component {
|
||||||
}, objectInspector)
|
}, objectInspector)
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return SplitBox({
|
||||||
sidebarVisible ?
|
className: "sidebar",
|
||||||
SplitBox({
|
endPanel,
|
||||||
className: "sidebar",
|
endPanelControl: true,
|
||||||
endPanel,
|
initialSize: "200px",
|
||||||
endPanelControl: true,
|
minSize: "100px",
|
||||||
initialSize: "200px",
|
vert: true,
|
||||||
minSize: "100px",
|
});
|
||||||
vert: true,
|
|
||||||
})
|
|
||||||
: null
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -314,6 +314,7 @@ skip-if = (os == 'linux') || (os == 'win' && os_version == '10.0' && debug && bi
|
||||||
[browser_webconsole_nodes_highlight.js]
|
[browser_webconsole_nodes_highlight.js]
|
||||||
[browser_webconsole_nodes_select.js]
|
[browser_webconsole_nodes_select.js]
|
||||||
[browser_webconsole_object_in_sidebar.js]
|
[browser_webconsole_object_in_sidebar.js]
|
||||||
|
[browser_webconsole_object_in_sidebar_keyboard_nav.js]
|
||||||
[browser_webconsole_object_inspector.js]
|
[browser_webconsole_object_inspector.js]
|
||||||
[browser_webconsole_object_inspector_entries.js]
|
[browser_webconsole_object_inspector_entries.js]
|
||||||
[browser_webconsole_object_inspector_key_sorting.js]
|
[browser_webconsole_object_inspector_key_sorting.js]
|
||||||
|
|
|
@ -0,0 +1,98 @@
|
||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
// Test that the ObjectInspector in the sidebar can be navigated with the keyboard.
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const TEST_URI = `data:text/html;charset=utf8,
|
||||||
|
<script>
|
||||||
|
console.log({
|
||||||
|
a:1,
|
||||||
|
b:2,
|
||||||
|
c: Array.from({length: 100}, (_, i) => i)
|
||||||
|
});
|
||||||
|
</script>`;
|
||||||
|
const {ELLIPSIS} = require("devtools/shared/l10n");
|
||||||
|
|
||||||
|
add_task(async function() {
|
||||||
|
// Should be removed when sidebar work is complete
|
||||||
|
await pushPref("devtools.webconsole.sidebarToggle", true);
|
||||||
|
|
||||||
|
let hud = await openNewTabAndConsole(TEST_URI);
|
||||||
|
|
||||||
|
let message = await waitFor(() => findMessage(hud, "Object"));
|
||||||
|
let object = message.querySelector(".object-inspector .objectBox-object");
|
||||||
|
|
||||||
|
const onSideBarVisible = waitFor(() =>
|
||||||
|
hud.ui.document.querySelector(".sidebar-contents"));
|
||||||
|
|
||||||
|
await openObjectInSidebar(hud, object);
|
||||||
|
const sidebarContents = await onSideBarVisible;
|
||||||
|
|
||||||
|
let objectInspector = sidebarContents.querySelector(".object-inspector");
|
||||||
|
ok(objectInspector, "The ObjectInspector is displayed");
|
||||||
|
|
||||||
|
// There are 5 nodes: the root, a, b, c, and proto.
|
||||||
|
await waitFor(() => objectInspector.querySelectorAll(".node").length === 5);
|
||||||
|
|
||||||
|
const [
|
||||||
|
root,
|
||||||
|
a,
|
||||||
|
b,
|
||||||
|
c,
|
||||||
|
] = objectInspector.querySelectorAll(".node");
|
||||||
|
|
||||||
|
ok(root.classList.contains("focused"), "The root node is focused");
|
||||||
|
|
||||||
|
await synthesizeKeyAndWaitForFocus("KEY_ArrowDown", a);
|
||||||
|
ok(true, "`a` node is focused");
|
||||||
|
|
||||||
|
await synthesizeKeyAndWaitForFocus("KEY_ArrowDown", b);
|
||||||
|
ok(true, "`b` node is focused");
|
||||||
|
|
||||||
|
await synthesizeKeyAndWaitForFocus("KEY_ArrowDown", c);
|
||||||
|
ok(true, "`c` node is focused");
|
||||||
|
|
||||||
|
EventUtils.synthesizeKey("KEY_ArrowRight");
|
||||||
|
await waitFor(() => objectInspector.querySelectorAll(".node").length > 5);
|
||||||
|
ok(true, "`c` node is expanded");
|
||||||
|
|
||||||
|
const arrayNodes = objectInspector.querySelectorAll(`[aria-level="3"]`);
|
||||||
|
await synthesizeKeyAndWaitForFocus("KEY_ArrowDown", arrayNodes[0]);
|
||||||
|
ok(true, "First item of the `c` array is focused");
|
||||||
|
|
||||||
|
await synthesizeKeyAndWaitForFocus("KEY_ArrowLeft", c);
|
||||||
|
ok(true, "`c` node is focused again");
|
||||||
|
|
||||||
|
await synthesizeKeyAndWaitForFocus("KEY_ArrowUp", b);
|
||||||
|
ok(true, "`b` node is focused again");
|
||||||
|
|
||||||
|
info("Select another object in the console output");
|
||||||
|
const onArrayMessage = waitForMessage(hud, "Array");
|
||||||
|
await ContentTask.spawn(gBrowser.selectedBrowser, null, () => {
|
||||||
|
content.wrappedJSObject.console.log([4, 5, 6]);
|
||||||
|
});
|
||||||
|
|
||||||
|
const arrayMessage = await onArrayMessage;
|
||||||
|
let array = arrayMessage.node.querySelector(".object-inspector .objectBox-array");
|
||||||
|
await openObjectInSidebar(hud, array);
|
||||||
|
|
||||||
|
await waitFor(() => sidebarContents.querySelector(".tree-node")
|
||||||
|
.textContent.includes(`(3) [${ELLIPSIS}]`));
|
||||||
|
ok(sidebarContents.querySelector(".tree-node").classList.contains("focused"),
|
||||||
|
"The root node of the new object in the sidebar is focused");
|
||||||
|
});
|
||||||
|
|
||||||
|
async function openObjectInSidebar(hud, objectNode) {
|
||||||
|
let contextMenu = await openContextMenu(hud, objectNode);
|
||||||
|
let openInSidebarEntry = contextMenu.querySelector("#console-menu-open-sidebar");
|
||||||
|
openInSidebarEntry.click();
|
||||||
|
await hideContextMenu(hud);
|
||||||
|
}
|
||||||
|
|
||||||
|
function synthesizeKeyAndWaitForFocus(keyStr, elementToBeFocused) {
|
||||||
|
let onFocusChanged = waitFor(() => elementToBeFocused.classList.contains("focused"));
|
||||||
|
EventUtils.synthesizeKey(keyStr);
|
||||||
|
return onFocusChanged;
|
||||||
|
}
|
|
@ -26,7 +26,7 @@ const { Grip } = REPS;
|
||||||
* @returns {ObjectInspector}
|
* @returns {ObjectInspector}
|
||||||
* An ObjectInspector for the given grip.
|
* An ObjectInspector for the given grip.
|
||||||
*/
|
*/
|
||||||
function getObjectInspector(grip, serviceContainer, override) {
|
function getObjectInspector(grip, serviceContainer, override = {}) {
|
||||||
let onDOMNodeMouseOver;
|
let onDOMNodeMouseOver;
|
||||||
let onDOMNodeMouseOut;
|
let onDOMNodeMouseOut;
|
||||||
let onInspectIconClick;
|
let onInspectIconClick;
|
||||||
|
@ -44,18 +44,12 @@ function getObjectInspector(grip, serviceContainer, override) {
|
||||||
: null;
|
: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const roots = createRootsFromGrip(grip);
|
||||||
|
|
||||||
const objectInspectorProps = {
|
const objectInspectorProps = {
|
||||||
autoExpandDepth: 0,
|
autoExpandDepth: 0,
|
||||||
mode: MODE.LONG,
|
mode: MODE.LONG,
|
||||||
// TODO: we disable focus since it's not currently working well in ObjectInspector.
|
roots,
|
||||||
// Let's remove the property below when problem are fixed in OI.
|
|
||||||
focusable: false,
|
|
||||||
roots: [{
|
|
||||||
path: (grip && grip.actor) || JSON.stringify(grip),
|
|
||||||
contents: {
|
|
||||||
value: grip
|
|
||||||
}
|
|
||||||
}],
|
|
||||||
createObjectClient: object =>
|
createObjectClient: object =>
|
||||||
new ObjectClient(serviceContainer.hudProxy.client, object),
|
new ObjectClient(serviceContainer.hudProxy.client, object),
|
||||||
createLongStringClient: object =>
|
createLongStringClient: object =>
|
||||||
|
@ -79,7 +73,22 @@ function getObjectInspector(grip, serviceContainer, override) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (override.autoFocusRoot) {
|
||||||
|
Object.assign(objectInspectorProps, {
|
||||||
|
focusedItem: roots[0]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return ObjectInspector({...objectInspectorProps, ...override});
|
return ObjectInspector({...objectInspectorProps, ...override});
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.getObjectInspector = getObjectInspector;
|
function createRootsFromGrip(grip) {
|
||||||
|
return [{
|
||||||
|
path: (grip && grip.actor) || JSON.stringify(grip),
|
||||||
|
contents: { value: grip }
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
getObjectInspector,
|
||||||
|
};
|
||||||
|
|
Загрузка…
Ссылка в новой задаче