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,