diff --git a/src/__tests__/inspectedElementSerializer.js b/src/__tests__/inspectedElementSerializer.js index 2423683c24..13e105cf0d 100644 --- a/src/__tests__/inspectedElementSerializer.js +++ b/src/__tests__/inspectedElementSerializer.js @@ -17,6 +17,7 @@ export function print(inspectedElement, serialize, indent) { id: inspectedElement.id, owners: inspectedElement.owners, context: inspectedElement.context, + events: inspectedElement.events, hooks: inspectedElement.hooks, props: inspectedElement.props, state: inspectedElement.state, diff --git a/src/backend/renderer.js b/src/backend/renderer.js index f4e8a6ffe9..6d23df5032 100644 --- a/src/backend/renderer.js +++ b/src/backend/renderer.js @@ -478,6 +478,10 @@ export function attach( case EVENT_TARGET_TOUCH_HIT_STRING: return 'TouchHitTarget'; default: + const displayName = elementType.displayName; + if (displayName !== undefined) { + return displayName; + } return 'EventTarget'; } case ForwardRef: @@ -1833,6 +1837,23 @@ export function attach( const isTimedOutSuspense = tag === SuspenseComponent && memoizedState !== null; + let events = null; + let node = fiber; + while (node !== null) { + if (node.tag === EventComponent) { + if (events === null) { + events = []; + } + const eventComponentInstance = node.stateNode; + const currentFiber = eventComponentInstance.currentFiber; + events.push({ + props: eventComponentInstance.props, + displayName: currentFiber.type.displayName, + }); + } + node = node.return; + } + return { id, @@ -1858,6 +1879,7 @@ export function attach( // Inspectable properties. // TODO Review sanitization approach for the below inspectable values. context, + events, hooks: usesHooks ? inspectHooksOfFiber(fiber, (renderer.currentDispatcherRef: any)) : null, @@ -1879,6 +1901,7 @@ export function attach( } // TODO Review sanitization approach for the below inspectable values. result.context = cleanForBridge(result.context); + result.events = cleanForBridge(result.events); result.hooks = cleanForBridge(result.hooks); result.props = cleanForBridge(result.props); result.state = cleanForBridge(result.state); diff --git a/src/devtools/views/Components/EventsTree.css b/src/devtools/views/Components/EventsTree.css new file mode 100644 index 0000000000..e89246aa1b --- /dev/null +++ b/src/devtools/views/Components/EventsTree.css @@ -0,0 +1,27 @@ +.EventsTree { + padding: 0.25rem; + border-top: 1px solid var(--color-border); +} +.EventsTree:first-of-type { + border-top: none; +} + +.HeaderRow { + display: flex; + align-items: center; +} + +.Header { + flex: 1 1; + font-family: var(--font-family-sans); +} + +.NameValueRow { + display: flex; +} + +.Empty { + color: var(--color-dimmer); + font-style: italic; + padding-left: 0.75rem; +} \ No newline at end of file diff --git a/src/devtools/views/Components/EventsTree.js b/src/devtools/views/Components/EventsTree.js new file mode 100644 index 0000000000..3c8635e627 --- /dev/null +++ b/src/devtools/views/Components/EventsTree.js @@ -0,0 +1,88 @@ +import { copy } from 'clipboard-js'; +import React, { useCallback, useState } from 'react'; +import styles from './EventsTree.css'; +import Button from '../Button'; +import ButtonIcon from '../ButtonIcon'; +import KeyValue from './KeyValue'; +import ExpandCollapseToggle from './ExpandCollapseToggle'; +import { serializeDataForCopy } from '../utils'; + +type Props = {| + events: Object, +|}; + +function EventsTreeView({ events }: Props) { + const handleCopy = useCallback(() => copy(serializeDataForCopy(events)), [ + events, + ]); + + return ( +