diff --git a/src/fb-stubs/AXLayoutExtender.js b/src/fb-stubs/AXLayoutExtender.js new file mode 100644 index 000000000..0c0dedf1b --- /dev/null +++ b/src/fb-stubs/AXLayoutExtender.js @@ -0,0 +1,25 @@ +/** + * Copyright 2018-present Facebook. + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * @format + */ + +import {Component} from 'react'; +import type {Element, ElementID, ElementSearchResultSet} from 'sonar'; + +export class AXElementsInspector extends Component<{ + onElementExpanded: (key: ElementID, deep: boolean) => void, + onElementSelected: (key: ElementID) => void, + onElementHovered: ?(key: ?ElementID) => void, + onValueChanged: ?(path: Array, val: any) => void, + selected: ?ElementID, + searchResults?: ?ElementSearchResultSet, + root: ?ElementID, + elements: {[key: ElementID]: Element}, + useAppSidebar?: boolean, +}> { + render() { + return null; + } +} diff --git a/src/plugins/layout/index.js b/src/plugins/layout/index.js index e39d9b64e..67c2afef7 100644 --- a/src/plugins/layout/index.js +++ b/src/plugins/layout/index.js @@ -24,15 +24,20 @@ import { SonarSidebar, } from 'sonar'; +import {AXElementsInspector} from '../../fb-stubs/AXLayoutExtender.js'; +import config from '../../fb-stubs/config.js'; + // $FlowFixMe import debounce from 'lodash.debounce'; export type InspectorState = {| initialised: boolean, selected: ?ElementID, + selectedAX: ?ElementID, root: ?ElementID, elements: {[key: ElementID]: Element}, isSearchActive: boolean, + inAXMode: boolean, searchResults: ?ElementSearchResultSet, outstandingSearchQuery: ?string, |}; @@ -140,8 +145,10 @@ export default class Layout extends SonarPlugin { elements: {}, initialised: false, isSearchActive: false, + inAXMode: false, root: null, selected: null, + selectedAX: null, searchResults: null, outstandingSearchQuery: null, }; @@ -153,6 +160,12 @@ export default class Layout extends SonarPlugin { }; }, + SelectAXElement(state: InspectorState, {key}: SelectElementArgs) { + return { + selectedAX: key, + }; + }, + ExpandElement(state: InspectorState, {expand, key}: ExpandElementArgs) { return { elements: { @@ -206,6 +219,10 @@ export default class Layout extends SonarPlugin { ) { return {isSearchActive}; }, + + SetAXMode(state: InspectorState, {inAXMode}: {inAXMode: boolean}) { + return {inAXMode}; + }, }; search(query: string) { @@ -481,6 +498,16 @@ export default class Layout extends SonarPlugin { this.client.send('setSearchActive', {active: isSearchActive}); }; + onTestToggleAccessibility = () => { + const inAXMode = !this.state.inAXMode; + this.dispatchAction({inAXMode, type: 'SetAXMode'}); + this.client + .call('testAccessibility', {active: inAXMode}) + .then(({message}: {message: string}) => { + console.log(message); + }); + }; + onElementSelected = debounce((key: ElementID) => { this.dispatchAction({key, type: 'SelectElement'}); this.client.send('setHighlighted', {id: key}); @@ -489,14 +516,25 @@ export default class Layout extends SonarPlugin { }); }); + onAXElementSelected = debounce((key: ElementID) => { + this.dispatchAction({key, type: 'SelectAXElement'}); + this.client.send('setHighlighted', {id: key}); + this.getNodes([key], true).then((elements: Array) => { + this.dispatchAction({elements, type: 'UpdateElements'}); + }); + }); + onElementHovered = debounce((key: ?ElementID) => { this.client.send('setHighlighted', {id: key}); }); onDataValueChanged = (path: Array, value: any) => { - this.client.send('setData', {id: this.state.selected, path, value}); + const selected = this.state.inAXMode + ? this.state.selectedAX + : this.state.selected; + this.client.send('setData', {id: selected, path, value}); this.props.logger.track('usage', 'layout:value-changed', { - id: this.state.selected, + id: selected, value: value, path: path, }); @@ -512,16 +550,42 @@ export default class Layout extends SonarPlugin { ) : null; }; + renderAXSidebar = () => { + return this.state.selectedAX != null ? ( + + ) : null; + }; + render() { const { initialised, selected, + selectedAX, root, elements, isSearchActive, + inAXMode, outstandingSearchQuery, } = this.state; + const AXInspector = ( + + ); + const AXButtonVisible = AXInspector !== null; + return ( @@ -540,6 +604,23 @@ export default class Layout extends SonarPlugin { } /> + {AXButtonVisible ? ( + + + + ) : null} { )} + {initialised && inAXMode ? AXInspector : null} - {this.renderSidebar()} + + {inAXMode ? this.renderAXSidebar() : this.renderSidebar()} + ); }