@samitouri / QOS-React-2 / commits / 779fd167ff

Adds inspectable event components + props

[Flare] Adds inspectable event components + props

Dominic Gannaway committed May 14, 2019 at 21:37 UTC 779fd167ffb004c337b5ba3d7923c2f18a96109a
7 files changed +144
src/__tests__/inspectedElementSerializer.js
+1
@@ -17,6 +17,7 @@ export function print(inspectedElement, serialize, indent) {
17 id: inspectedElement.id,
18 owners: inspectedElement.owners,
19 context: inspectedElement.context,
20 + events: inspectedElement.events,
21 hooks: inspectedElement.hooks,
22 props: inspectedElement.props,
23 state: inspectedElement.state,
src/backend/renderer.js
+23
@@ -478,6 +478,10 @@ export function attach(
478 case EVENT_TARGET_TOUCH_HIT_STRING:
479 return 'TouchHitTarget';
480 default:
481 + const displayName = elementType.displayName;
482 + if (displayName !== undefined) {
483 + return displayName;
484 + }
485 return 'EventTarget';
486 }
487 case ForwardRef:
@@ -1833,6 +1837,23 @@ export function attach(
1837 const isTimedOutSuspense =
1838 tag === SuspenseComponent && memoizedState !== null;
1839
1840 + let events = null;
1841 + let node = fiber;
1842 + while (node !== null) {
1843 + if (node.tag === EventComponent) {
1844 + if (events === null) {
1845 + events = [];
1846 + }
1847 + const eventComponentInstance = node.stateNode;
1848 + const currentFiber = eventComponentInstance.currentFiber;
1849 + events.push({
1850 + props: eventComponentInstance.props,
1851 + displayName: currentFiber.type.displayName,
1852 + });
1853 + }
1854 + node = node.return;
1855 + }
1856 +
1857 return {
1858 id,
1859
@@ -1858,6 +1879,7 @@ export function attach(
1879 // Inspectable properties.
1880 // TODO Review sanitization approach for the below inspectable values.
1881 context,
1882 + events,
1883 hooks: usesHooks
1884 ? inspectHooksOfFiber(fiber, (renderer.currentDispatcherRef: any))
1885 : null,
@@ -1879,6 +1901,7 @@ export function attach(
1901 }
1902 // TODO Review sanitization approach for the below inspectable values.
1903 result.context = cleanForBridge(result.context);
1904 + result.events = cleanForBridge(result.events);
1905 result.hooks = cleanForBridge(result.hooks);
1906 result.props = cleanForBridge(result.props);
1907 result.state = cleanForBridge(result.state);
src/devtools/views/Components/EventsTree.css new
+27
@@ -0,0 +1,27 @@
1 +.EventsTree {
2 + padding: 0.25rem;
3 + border-top: 1px solid var(--color-border);
4 +}
5 +.EventsTree:first-of-type {
6 + border-top: none;
7 +}
8 +
9 +.HeaderRow {
10 + display: flex;
11 + align-items: center;
12 +}
13 +
14 +.Header {
15 + flex: 1 1;
16 + font-family: var(--font-family-sans);
17 +}
18 +
19 +.NameValueRow {
20 + display: flex;
21 +}
22 +
23 +.Empty {
24 + color: var(--color-dimmer);
25 + font-style: italic;
26 + padding-left: 0.75rem;
27 +}
\ No newline at end of file
src/devtools/views/Components/EventsTree.js new
+88
@@ -0,0 +1,88 @@
1 +import { copy } from 'clipboard-js';
2 +import React, { useCallback, useState } from 'react';
3 +import styles from './EventsTree.css';
4 +import Button from '../Button';
5 +import ButtonIcon from '../ButtonIcon';
6 +import KeyValue from './KeyValue';
7 +import ExpandCollapseToggle from './ExpandCollapseToggle';
8 +import { serializeDataForCopy } from '../utils';
9 +
10 +type Props = {|
11 + events: Object,
12 +|};
13 +
14 +function EventsTreeView({ events }: Props) {
15 + const handleCopy = useCallback(() => copy(serializeDataForCopy(events)), [
16 + events,
17 + ]);
18 +
19 + return (
20 + <div className={styles.EventsTree}>
21 + <div className={styles.HeaderRow}>
22 + <div className={styles.Header}>events</div>
23 + {
24 + <Button onClick={handleCopy} title="Copy to clipboard">
25 + <ButtonIcon type="copy" />
26 + </Button>
27 + }
28 + </div>
29 + <InnerEventsTreeView events={events} />
30 + </div>
31 + );
32 +}
33 +
34 +function InnerEventsTreeView({ events }: Props) {
35 + return events.map((event, index) => (
36 + <EventComponentView
37 + key={index}
38 + displayName={event.displayName}
39 + props={event.props}
40 + />
41 + ));
42 +}
43 +
44 +type EventComponentViewProps = {|
45 + displayName: string,
46 + props: null | Object,
47 +|};
48 +
49 +function EventComponentView({ displayName, props }: EventComponentViewProps) {
50 + const [isOpen, setIsOpen] = useState(false);
51 + let eventComponentProps = null;
52 + // eslint-disable-next-line no-unused-vars
53 + let children;
54 +
55 + if (props !== null) {
56 + // We don't want children, so extract it out
57 + ({ children, ...eventComponentProps } = props);
58 + }
59 +
60 + return (
61 + <div>
62 + <div className={styles.NameValueRow}>
63 + <ExpandCollapseToggle isOpen={isOpen} setIsOpen={setIsOpen} />
64 + <span onClick={() => {}} className={styles.Name}>
65 + {displayName}
66 + </span>
67 + </div>
68 + <div className={styles.Children} hidden={!isOpen}>
69 + {eventComponentProps === null && (
70 + <div className={styles.Empty}>None</div>
71 + )}
72 + {eventComponentProps !== null &&
73 + Object.keys((eventComponentProps: any)).map(name => (
74 + <KeyValue
75 + key={name}
76 + depth={1}
77 + name={name}
78 + path={[name]}
79 + value={(eventComponentProps: any)[name]}
80 + />
81 + ))}
82 + </div>
83 + </div>
84 + );
85 +}
86 +
87 +// $FlowFixMe
88 +export default React.memo(EventsTreeView);
src/devtools/views/Components/InspectedElementContext.js
+1
@@ -90,6 +90,7 @@ function InspectedElementContextController({ children }: Props) {
90 inspectedElement = (({
91 ...inspectedElement,
92 context: hydrateHelper(inspectedElement.context),
93 + events: hydrateHelper(inspectedElement.events),
94 hooks: hydrateHelper(inspectedElement.hooks),
95 props: hydrateHelper(inspectedElement.props),
96 state: hydrateHelper(inspectedElement.state),
src/devtools/views/Components/SelectedElement.js
+3
@@ -6,6 +6,7 @@ import { BridgeContext, StoreContext } from '../context';
6 import Button from '../Button';
7 import ButtonIcon from '../ButtonIcon';
8 import HooksTree from './HooksTree';
9 +import EventsTree from './EventsTree';
10 import { ModalDialogContext } from '../ModalDialog';
11 import InspectedElementTree from './InspectedElementTree';
12 import { InspectedElementContext } from './InspectedElementContext';
@@ -221,6 +222,7 @@ function InspectedElementView({
222 canEditHooks,
223 canToggleSuspense,
224 context,
225 + events,
226 hooks,
227 owners,
228 props,
@@ -297,6 +299,7 @@ function InspectedElementView({
299 data={context}
300 overrideValueFn={overrideContextFn}
301 />
302 + {events !== null && events.length > 0 && <EventsTree events={events} />}
303
304 {ownerID === null && owners !== null && owners.length > 0 && (
305 <div className={styles.Owners}>
src/devtools/views/Components/types.js
+1
@@ -59,6 +59,7 @@ export type InspectedElement = {|
59
60 // Inspectable properties.
61 context: Object | null,
62 + events: Object | null,
63 hooks: Object | null,
64 props: Object | null,
65 state: Object | null,