Cleanup js in layout inspector, add check for ios
Summary: Clean up javascript. Add a check to remove ax mode toggle if using ios. Add safety check for extraInfo where it hasn't been added yet. Reviewed By: danielbuechele Differential Revision: D9070574 fbshipit-source-id: 49ac781c01ea47239d6c24089976497371973726
This commit is contained in:
committed by
Facebook Github Bot
parent
06e70a1555
commit
30a19901ee
@@ -35,23 +35,25 @@ import debounce from 'lodash.debounce';
|
|||||||
|
|
||||||
export type InspectorState = {|
|
export type InspectorState = {|
|
||||||
initialised: boolean,
|
initialised: boolean,
|
||||||
AXinitialised: boolean,
|
|
||||||
selected: ?ElementID,
|
selected: ?ElementID,
|
||||||
AXselected: ?ElementID,
|
|
||||||
AXfocused: ?ElementID,
|
|
||||||
root: ?ElementID,
|
root: ?ElementID,
|
||||||
AXroot: ?ElementID,
|
|
||||||
elements: {[key: ElementID]: Element},
|
elements: {[key: ElementID]: Element},
|
||||||
AXelements: {[key: ElementID]: Element},
|
|
||||||
isSearchActive: boolean,
|
isSearchActive: boolean,
|
||||||
inAXMode: boolean,
|
|
||||||
searchResults: ?ElementSearchResultSet,
|
searchResults: ?ElementSearchResultSet,
|
||||||
outstandingSearchQuery: ?string,
|
outstandingSearchQuery: ?string,
|
||||||
|
// properties for ax mode
|
||||||
|
AXinitialised: boolean,
|
||||||
|
AXselected: ?ElementID,
|
||||||
|
AXfocused: ?ElementID,
|
||||||
|
AXroot: ?ElementID,
|
||||||
|
AXelements: {[key: ElementID]: Element},
|
||||||
|
inAXMode: boolean,
|
||||||
AXtoNonAXMapping: {[key: ElementID]: ElementID},
|
AXtoNonAXMapping: {[key: ElementID]: ElementID},
|
||||||
|};
|
|};
|
||||||
|
|
||||||
type SelectElementArgs = {|
|
type SelectElementArgs = {|
|
||||||
key: ElementID,
|
key: ElementID,
|
||||||
|
AXkey: ElementID,
|
||||||
|};
|
|};
|
||||||
|
|
||||||
type ExpandElementArgs = {|
|
type ExpandElementArgs = {|
|
||||||
@@ -84,6 +86,11 @@ type GetNodesResult = {|
|
|||||||
elements: Array<Element>,
|
elements: Array<Element>,
|
||||||
|};
|
|};
|
||||||
|
|
||||||
|
type GetNodesOptions = {|
|
||||||
|
force: boolean,
|
||||||
|
ax: boolean,
|
||||||
|
|};
|
||||||
|
|
||||||
type SearchResultTree = {|
|
type SearchResultTree = {|
|
||||||
id: string,
|
id: string,
|
||||||
isMatch: Boolean,
|
isMatch: Boolean,
|
||||||
@@ -160,53 +167,28 @@ export default class Layout extends SonarPlugin<InspectorState> {
|
|||||||
|
|
||||||
state = {
|
state = {
|
||||||
elements: {},
|
elements: {},
|
||||||
AXelements: {},
|
|
||||||
initialised: false,
|
initialised: false,
|
||||||
AXinitialised: false,
|
|
||||||
isSearchActive: false,
|
isSearchActive: false,
|
||||||
inAXMode: false,
|
|
||||||
root: null,
|
root: null,
|
||||||
AXroot: null,
|
|
||||||
selected: null,
|
selected: null,
|
||||||
AXselected: null,
|
|
||||||
AXfocused: null,
|
|
||||||
searchResults: null,
|
searchResults: null,
|
||||||
outstandingSearchQuery: null,
|
outstandingSearchQuery: null,
|
||||||
|
// properties for ax mode
|
||||||
|
inAXMode: false,
|
||||||
|
AXelements: {},
|
||||||
|
AXinitialised: false,
|
||||||
|
AXroot: null,
|
||||||
|
AXselected: null,
|
||||||
|
AXfocused: null,
|
||||||
AXtoNonAXMapping: {},
|
AXtoNonAXMapping: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
reducers = {
|
reducers = {
|
||||||
SelectElement(state: InspectorState, {key}: SelectElementArgs) {
|
SelectElement(state: InspectorState, {key, AXkey}: SelectElementArgs) {
|
||||||
const linkedAXNode =
|
|
||||||
state.elements[key] &&
|
|
||||||
state.elements[key].extraInfo &&
|
|
||||||
state.elements[key].extraInfo.linkedAXNode;
|
|
||||||
|
|
||||||
// element only in main tree with linkedAXNode selected
|
|
||||||
if (linkedAXNode) {
|
|
||||||
return {
|
return {
|
||||||
selected: key,
|
selected: key,
|
||||||
AXselected: linkedAXNode,
|
AXselected: AXkey,
|
||||||
};
|
};
|
||||||
|
|
||||||
// element only in AX tree with linked nonAX element selected
|
|
||||||
} else if (
|
|
||||||
(!state.elements[key] ||
|
|
||||||
state.elements[key].name === 'ComponentHost') &&
|
|
||||||
state.AXtoNonAXMapping[key]
|
|
||||||
) {
|
|
||||||
return {
|
|
||||||
selected: state.AXtoNonAXMapping[key],
|
|
||||||
AXselected: key,
|
|
||||||
};
|
|
||||||
|
|
||||||
// keys are same for both trees or 'linked' element does not exist
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
selected: key,
|
|
||||||
AXselected: key,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
ExpandElement(state: InspectorState, {expand, key}: ExpandElementArgs) {
|
ExpandElement(state: InspectorState, {expand, key}: ExpandElementArgs) {
|
||||||
@@ -218,13 +200,6 @@ export default class Layout extends SonarPlugin<InspectorState> {
|
|||||||
expanded: expand,
|
expanded: expand,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
AXelements: {
|
|
||||||
...state.AXelements,
|
|
||||||
[key]: {
|
|
||||||
...state.AXelements[key],
|
|
||||||
expanded: expand,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -301,7 +276,7 @@ export default class Layout extends SonarPlugin<InspectorState> {
|
|||||||
let updatedFocus = forFocusEvent ? null : state.AXfocused;
|
let updatedFocus = forFocusEvent ? null : state.AXfocused;
|
||||||
|
|
||||||
for (const element of elements) {
|
for (const element of elements) {
|
||||||
if (element.extraInfo.focused) {
|
if (element.extraInfo && element.extraInfo.focused) {
|
||||||
updatedFocus = element.id;
|
updatedFocus = element.id;
|
||||||
}
|
}
|
||||||
const current = updatedElements[element.id] || {};
|
const current = updatedElements[element.id] || {};
|
||||||
@@ -447,18 +422,12 @@ export default class Layout extends SonarPlugin<InspectorState> {
|
|||||||
return elements;
|
return elements;
|
||||||
}
|
}
|
||||||
|
|
||||||
init() {
|
axEnabled(): boolean {
|
||||||
performance.mark('LayoutInspectorInitialize');
|
// only visible internally for Android clients
|
||||||
this.client.call('getRoot').then((element: Element) => {
|
return AXToggleButtonEnabled && this.realClient.query.os === 'Android';
|
||||||
this.dispatchAction({elements: [element], type: 'UpdateElements'});
|
}
|
||||||
this.dispatchAction({root: element.id, type: 'SetRoot'});
|
|
||||||
this.performInitialExpand(element, false).then(() => {
|
|
||||||
this.props.logger.trackTimeSince('LayoutInspectorInitialize');
|
|
||||||
this.setState({initialised: true});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
if (AXToggleButtonEnabled) {
|
initAX() {
|
||||||
this.client.call('getAXRoot').then((element: Element) => {
|
this.client.call('getAXRoot').then((element: Element) => {
|
||||||
this.dispatchAction({elements: [element], type: 'UpdateAXElements'});
|
this.dispatchAction({elements: [element], type: 'UpdateAXElements'});
|
||||||
this.dispatchAction({root: element.id, type: 'SetAXRoot'});
|
this.dispatchAction({root: element.id, type: 'SetAXRoot'});
|
||||||
@@ -466,15 +435,11 @@ export default class Layout extends SonarPlugin<InspectorState> {
|
|||||||
this.setState({AXinitialised: true});
|
this.setState({AXinitialised: true});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
this.client.subscribe('axFocusEvent', (focusEvent: AXFocusEventResult) => {
|
this.client.subscribe('axFocusEvent', (focusEvent: AXFocusEventResult) => {
|
||||||
if (AXToggleButtonEnabled) {
|
|
||||||
// if focusing, need to update all elements in the tree because
|
// if focusing, need to update all elements in the tree because
|
||||||
// we don't know which one now has focus
|
// we don't know which one now has focus
|
||||||
const keys = focusEvent.isFocus
|
const keys = focusEvent.isFocus ? Object.keys(this.state.AXelements) : [];
|
||||||
? Object.keys(this.state.AXelements)
|
|
||||||
: [];
|
|
||||||
|
|
||||||
// if unfocusing and currently focused element exists, update only the
|
// if unfocusing and currently focused element exists, update only the
|
||||||
// focused element (and only if it is loaded in tree)
|
// focused element (and only if it is loaded in tree)
|
||||||
@@ -486,14 +451,27 @@ export default class Layout extends SonarPlugin<InspectorState> {
|
|||||||
keys.push(this.state.AXfocused);
|
keys.push(this.state.AXfocused);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.getNodes(keys, true, true).then((elements: Array<Element>) => {
|
this.getNodes(keys, {force: true, ax: true}).then(
|
||||||
|
(elements: Array<Element>) => {
|
||||||
this.dispatchAction({
|
this.dispatchAction({
|
||||||
elements,
|
elements,
|
||||||
forFocusEvent: true,
|
forFocusEvent: true,
|
||||||
type: 'UpdateAXElements',
|
type: 'UpdateAXElements',
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
init() {
|
||||||
|
performance.mark('LayoutInspectorInitialize');
|
||||||
|
this.client.call('getRoot').then((element: Element) => {
|
||||||
|
this.dispatchAction({elements: [element], type: 'UpdateElements'});
|
||||||
|
this.dispatchAction({root: element.id, type: 'SetRoot'});
|
||||||
|
this.performInitialExpand(element, false).then(() => {
|
||||||
|
this.props.logger.trackTimeSince('LayoutInspectorInitialize');
|
||||||
|
this.setState({initialised: true});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
this.client.subscribe(
|
this.client.subscribe(
|
||||||
@@ -530,6 +508,10 @@ export default class Layout extends SonarPlugin<InspectorState> {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (this.axEnabled()) {
|
||||||
|
this.initAX();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
invalidate(ids: Array<ElementID>): Promise<Array<Element>> {
|
invalidate(ids: Array<ElementID>): Promise<Array<Element>> {
|
||||||
@@ -538,7 +520,8 @@ export default class Layout extends SonarPlugin<InspectorState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const ax = this.state.inAXMode;
|
const ax = this.state.inAXMode;
|
||||||
return this.getNodes(ids, true, ax).then((elements: Array<Element>) => {
|
return this.getNodes(ids, {force: true, ax}).then(
|
||||||
|
(elements: Array<Element>) => {
|
||||||
const children = elements
|
const children = elements
|
||||||
.filter(element => {
|
.filter(element => {
|
||||||
const prev = (ax ? this.state.AXelements : this.state.elements)[
|
const prev = (ax ? this.state.AXelements : this.state.elements)[
|
||||||
@@ -552,39 +535,42 @@ export default class Layout extends SonarPlugin<InspectorState> {
|
|||||||
return Promise.all([elements, this.invalidate(children)]).then(arr => {
|
return Promise.all([elements, this.invalidate(children)]).then(arr => {
|
||||||
return arr.reduce((acc, val) => acc.concat(val), []);
|
return arr.reduce((acc, val) => acc.concat(val), []);
|
||||||
});
|
});
|
||||||
});
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getNodesAndDirectChildren(
|
getNodesAndDirectChildren(
|
||||||
ids: Array<ElementID>,
|
ids: Array<ElementID>,
|
||||||
ax: boolean, // always false at the moment bc only used for select
|
ax: boolean, // always false at the moment bc only used for select
|
||||||
): Promise<Array<Element>> {
|
): Promise<Array<Element>> {
|
||||||
return this.getNodes(ids, false, ax).then((elements: Array<Element>) => {
|
return this.getNodes(ids, {force: false, ax}).then(
|
||||||
|
(elements: Array<Element>) => {
|
||||||
const children = elements
|
const children = elements
|
||||||
.map(element => element.children)
|
.map(element => element.children)
|
||||||
.reduce((acc, val) => acc.concat(val), []);
|
.reduce((acc, val) => acc.concat(val), []);
|
||||||
|
|
||||||
return Promise.all([elements, this.getNodes(children, false, ax)]).then(
|
return Promise.all([
|
||||||
arr => {
|
elements,
|
||||||
|
this.getNodes(children, {force: false, ax}),
|
||||||
|
]).then(arr => {
|
||||||
return arr.reduce((acc, val) => acc.concat(val), []);
|
return arr.reduce((acc, val) => acc.concat(val), []);
|
||||||
|
});
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getChildren(key: ElementID, ax: boolean): Promise<Array<Element>> {
|
getChildren(key: ElementID, ax: boolean): Promise<Array<Element>> {
|
||||||
return this.getNodes(
|
return this.getNodes(
|
||||||
(ax ? this.state.AXelements : this.state.elements)[key].children,
|
(ax ? this.state.AXelements : this.state.elements)[key].children,
|
||||||
false,
|
{force: false, ax},
|
||||||
ax,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getNodes(
|
getNodes(
|
||||||
ids: Array<ElementID> = [],
|
ids: Array<ElementID> = [],
|
||||||
force: boolean,
|
options: GetNodesOptions,
|
||||||
ax: boolean,
|
|
||||||
): Promise<Array<Element>> {
|
): Promise<Array<Element>> {
|
||||||
|
const {force, ax} = options;
|
||||||
if (!force) {
|
if (!force) {
|
||||||
ids = ids.filter(id => {
|
ids = ids.filter(id => {
|
||||||
return (
|
return (
|
||||||
@@ -639,7 +625,7 @@ export default class Layout extends SonarPlugin<InspectorState> {
|
|||||||
if (this.state.inAXMode && !ax) {
|
if (this.state.inAXMode && !ax) {
|
||||||
// expand child wrapper elements that aren't in the AX tree (e.g. fragments)
|
// expand child wrapper elements that aren't in the AX tree (e.g. fragments)
|
||||||
for (const childElem of elements) {
|
for (const childElem of elements) {
|
||||||
if (childElem.extraInfo.nonAXWithAXChild) {
|
if (childElem.extraInfo && childElem.extraInfo.nonAXWithAXChild) {
|
||||||
this.setElementExpanded(childElem.id, true, false);
|
this.setElementExpanded(childElem.id, true, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -707,15 +693,51 @@ export default class Layout extends SonarPlugin<InspectorState> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
onElementSelected = debounce((key: ElementID) => {
|
onElementSelected = debounce((key: ElementID) => {
|
||||||
this.dispatchAction({key, type: 'SelectElement'});
|
let finalKey = key;
|
||||||
|
let finalAXkey = null;
|
||||||
|
|
||||||
|
if (this.axEnabled()) {
|
||||||
|
const linkedAXNode =
|
||||||
|
this.state.elements[key] &&
|
||||||
|
this.state.elements[key].extraInfo &&
|
||||||
|
this.state.elements[key].extraInfo.linkedAXNode;
|
||||||
|
|
||||||
|
// element only in main tree with linkedAXNode selected
|
||||||
|
if (linkedAXNode) {
|
||||||
|
finalAXkey = linkedAXNode;
|
||||||
|
|
||||||
|
// element only in AX tree with linked nonAX (litho) element selected
|
||||||
|
} else if (
|
||||||
|
(!this.state.elements[key] ||
|
||||||
|
this.state.elements[key].name === 'ComponentHost') &&
|
||||||
|
this.state.AXtoNonAXMapping[key]
|
||||||
|
) {
|
||||||
|
finalKey = this.state.AXtoNonAXMapping[key];
|
||||||
|
finalAXkey = key;
|
||||||
|
|
||||||
|
// keys are same for both trees or 'linked' element does not exist
|
||||||
|
} else {
|
||||||
|
finalAXkey = key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.dispatchAction({
|
||||||
|
key: finalKey,
|
||||||
|
AXkey: finalAXkey,
|
||||||
|
type: 'SelectElement',
|
||||||
|
});
|
||||||
this.client.send('setHighlighted', {id: key});
|
this.client.send('setHighlighted', {id: key});
|
||||||
this.getNodes([key], true, false).then((elements: Array<Element>) => {
|
this.getNodes([finalKey], {force: true, ax: false}).then(
|
||||||
|
(elements: Array<Element>) => {
|
||||||
this.dispatchAction({elements, type: 'UpdateElements'});
|
this.dispatchAction({elements, type: 'UpdateElements'});
|
||||||
});
|
},
|
||||||
if (AXToggleButtonEnabled) {
|
);
|
||||||
this.getNodes([key], true, true).then((elements: Array<Element>) => {
|
if (this.axEnabled() && finalAXkey) {
|
||||||
|
this.getNodes([finalAXkey], {force: true, ax: true}).then(
|
||||||
|
(elements: Array<Element>) => {
|
||||||
this.dispatchAction({elements, type: 'UpdateAXElements'});
|
this.dispatchAction({elements, type: 'UpdateAXElements'});
|
||||||
});
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -795,7 +817,7 @@ export default class Layout extends SonarPlugin<InspectorState> {
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</SearchIconContainer>
|
</SearchIconContainer>
|
||||||
{AXToggleButtonEnabled ? (
|
{this.axEnabled() ? (
|
||||||
<SearchIconContainer
|
<SearchIconContainer
|
||||||
onClick={this.onToggleAccessibility}
|
onClick={this.onToggleAccessibility}
|
||||||
role="button"
|
role="button"
|
||||||
|
|||||||
@@ -39,7 +39,8 @@ const ElementsRowContainer = ContextMenu.extends(
|
|||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
color: props => (props.selected ? colors.white : colors.grapeDark3),
|
color: props =>
|
||||||
|
props.selected || props.focused ? colors.white : colors.grapeDark3,
|
||||||
flexShrink: 0,
|
flexShrink: 0,
|
||||||
flexWrap: 'nowrap',
|
flexWrap: 'nowrap',
|
||||||
height: ROW_HEIGHT,
|
height: ROW_HEIGHT,
|
||||||
@@ -66,7 +67,7 @@ const ElementsRowContainer = ContextMenu.extends(
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ignoreAttributes: ['level', 'selected', 'even'],
|
ignoreAttributes: ['level', 'selected', 'even', 'focused'],
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -132,9 +133,7 @@ class PartialHighlight extends PureComponent<{
|
|||||||
static HighlightedText = styled.text({
|
static HighlightedText = styled.text({
|
||||||
backgroundColor: '#ffff33',
|
backgroundColor: '#ffff33',
|
||||||
color: props =>
|
color: props =>
|
||||||
props.selected || props.focused
|
props.selected ? `${colors.grapeDark3} !important` : 'auto',
|
||||||
? `${colors.grapeDark3} !important`
|
|
||||||
: 'auto',
|
|
||||||
});
|
});
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@@ -174,7 +173,6 @@ class ElementsRowAttribute extends PureComponent<{
|
|||||||
value: string,
|
value: string,
|
||||||
matchingSearchQuery: ?string,
|
matchingSearchQuery: ?string,
|
||||||
selected: boolean,
|
selected: boolean,
|
||||||
focused: boolean,
|
|
||||||
}> {
|
}> {
|
||||||
render() {
|
render() {
|
||||||
const {name, value, matchingSearchQuery, selected} = this.props;
|
const {name, value, matchingSearchQuery, selected} = this.props;
|
||||||
@@ -296,7 +294,7 @@ class ElementsRow extends PureComponent<ElementsRowProps, ElementsRowState> {
|
|||||||
<Glyph
|
<Glyph
|
||||||
size={8}
|
size={8}
|
||||||
name={element.expanded ? 'chevron-down' : 'chevron-right'}
|
name={element.expanded ? 'chevron-down' : 'chevron-right'}
|
||||||
color={selected ? 'white' : colors.light80}
|
color={selected || focused ? 'white' : colors.light80}
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user