Bug 1424159 - make trees in console keyboard accessible. r=nchevobbe
authorYura Zenevich <yura.zenevich@gmail.com>
Fri, 01 Mar 2019 08:16:44 +0000
changeset 519865 413a18c16f0e08f8789a9bb61f5b894a95ea7a08
parent 519864 ad25e831bd26dae15f44c7224004749a13099032
child 519866 290bdcdf2b6336e218e8c2bf441dcfaddc26586c
push id10862
push userffxbld-merge
push dateMon, 11 Mar 2019 13:01:11 +0000
treeherdermozilla-beta@a2e7f5c935da [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnchevobbe
bugs1424159
milestone67.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1424159 - make trees in console keyboard accessible. r=nchevobbe Differential Revision: https://phabricator.services.mozilla.com/D21546
devtools/client/shared/components/reps/reps.js
devtools/client/webconsole/components/GripMessageBody.js
--- a/devtools/client/shared/components/reps/reps.js
+++ b/devtools/client/shared/components/reps/reps.js
@@ -163,16 +163,17 @@ class TreeNode extends Component {
       index: _propTypes2.default.number.isRequired,
       depth: _propTypes2.default.number.isRequired,
       focused: _propTypes2.default.bool.isRequired,
       active: _propTypes2.default.bool.isRequired,
       expanded: _propTypes2.default.bool.isRequired,
       item: _propTypes2.default.any.isRequired,
       isExpandable: _propTypes2.default.bool.isRequired,
       onClick: _propTypes2.default.func,
+      shouldItemUpdate: _propTypes2.default.func,
       renderItem: _propTypes2.default.func.isRequired
     };
   }
 
   constructor(props) {
     super(props);
 
     this.treeNodeRef = _react2.default.createRef();
@@ -191,17 +192,17 @@ class TreeNode extends Component {
         elms[0].focus();
       }
     } else {
       elms.forEach(elm => elm.setAttribute("tabindex", "-1"));
     }
   }
 
   shouldComponentUpdate(nextProps) {
-    return this.props.item !== nextProps.item || this.props.focused !== nextProps.focused || this.props.expanded !== nextProps.expanded;
+    return this.props.item !== nextProps.item || (this.props.shouldItemUpdate && this.props.shouldItemUpdate(this.props.item, nextProps.item)) || this.props.focused !== nextProps.focused || this.props.expanded !== nextProps.expanded;
   }
 
   /**
    * Get a list of all elements that are focusable with a keyboard inside the
    * tree node.
    */
   getFocusableElements() {
     return this.treeNodeRef.current ? Array.from(this.treeNodeRef.current.querySelectorAll(FOCUSABLE_SELECTOR)) : [];
@@ -418,16 +419,27 @@ class Tree extends Component {
       // Type: getChildren(item: Item) -> [Item]
       //
       // Example:
       //
       //     // This item's children are stored in its `children` property.
       //     getChildren: item => item.children
       getChildren: _propTypes2.default.func.isRequired,
 
+      // A function to check if the tree node for the item should be updated.
+      //
+      // Type: shouldItemUpdate(prevItem: Item, nextItem: Item) -> Boolean
+      //
+      // Example:
+      //
+      //     // This item should be updated if it's type is a long string
+      //     shouldItemUpdate: (prevItem, nextItem) =>
+      //       nextItem.type === "longstring"
+      shouldItemUpdate: _propTypes2.default.func,
+
       // A function which takes an item and ArrowExpander component instance and
       // returns a component, or text, or anything else that React considers
       // renderable.
       //
       // Type: renderItem(item: Item,
       //                  depth: Number,
       //                  isFocused: Boolean,
       //                  arrow: ReactComponent,
@@ -988,16 +1000,17 @@ class Tree extends Component {
         // We make a key unique depending on whether the tree node is in active
         // or inactive state to make sure that it is actually replaced and the
         // tabbable state is reset.
         key: `${key}-${active === item ? "active" : "inactive"}`,
         id: key,
         index: i,
         item,
         depth,
+        shouldItemUpdate: this.props.shouldItemUpdate,
         renderItem: this.props.renderItem,
         focused: focused === item,
         active: active === item,
         expanded: this.props.isExpanded(item),
         isExpandable: this._nodeIsExpandable(item),
         onExpand: this._onExpand,
         onCollapse: this._onCollapse,
         onClick: e => {
@@ -3586,16 +3599,17 @@ const ObjectInspectorItem = createFactor
 const classnames = __webpack_require__(6);
 
 const Utils = __webpack_require__(61);
 const { renderRep, shouldRenderRootsInReps } = Utils;
 const {
   getChildrenWithEvaluations,
   getActor,
   getParent,
+  getValue,
   nodeIsPrimitive,
   nodeHasGetter,
   nodeHasSetter
 } = Utils.node;
 
 // This implements a component that renders an interactive inspector
 // for looking at JavaScript objects. It expects descriptions of
 // objects from the protocol, and will dynamically fetch children
@@ -3809,29 +3823,35 @@ class ObjectInspector extends Component 
       getChildren: this.getItemChildren,
       getKey: this.getNodeKey,
 
       onExpand: item => this.setExpanded(item, true),
       onCollapse: item => this.setExpanded(item, false),
       onFocus: focusable ? this.focusItem : null,
       onActivate: focusable ? this.activateItem : null,
 
+      shouldItemUpdate,
       renderItem: (item, depth, focused, arrow, expanded) => ObjectInspectorItem({
         ...this.props,
         item,
         depth,
         focused,
         arrow,
         expanded,
         setExpanded: this.setExpanded
       })
     });
   }
 }
 
+function shouldItemUpdate(prevItem, nextItem) {
+  const value = getValue(nextItem);
+  return value && value.type === "longString";
+}
+
 function mapStateToProps(state, props) {
   return {
     expandedPaths: selectors.getExpandedPaths(state),
     loadedProperties: selectors.getLoadedProperties(state),
     evaluations: selectors.getEvaluations(state)
   };
 }
 
--- a/devtools/client/webconsole/components/GripMessageBody.js
+++ b/devtools/client/webconsole/components/GripMessageBody.js
@@ -58,20 +58,16 @@ function GripMessageBody(props) {
   if (userProvidedStyle && userProvidedStyle !== "") {
     styleObject = cleanupStyle(userProvidedStyle, serviceContainer.createElement);
   }
 
   const objectInspectorProps = {
     autoExpandDepth: shouldAutoExpandObjectInspector(props) ? 1 : 0,
     mode,
     maybeScrollToBottom,
-    // 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,
     onCmdCtrlClick: (node, { depth, event, focused, expanded }) => {
       const value = utils.node.getValue(node);
       if (value) {
         dispatch(actions.showObjectInSidebar(value));
       }
     },
   };