diff --git a/devtools/client/netmonitor/src/assets/styles/websockets.css b/devtools/client/netmonitor/src/assets/styles/websockets.css index 481244907b58..a108debdb06a 100644 --- a/devtools/client/netmonitor/src/assets/styles/websockets.css +++ b/devtools/client/netmonitor/src/assets/styles/websockets.css @@ -145,3 +145,7 @@ #messages-panel .truncation-checkbox { margin-right: 5px; } + +#messages-panel .truncated-message { + font-variant-numeric: tabular-nums; +} diff --git a/devtools/client/netmonitor/src/components/websockets/FrameListContent.js b/devtools/client/netmonitor/src/components/websockets/FrameListContent.js index 67ce52a8d1d4..f826923f291b 100644 --- a/devtools/client/netmonitor/src/components/websockets/FrameListContent.js +++ b/devtools/client/netmonitor/src/components/websockets/FrameListContent.js @@ -48,6 +48,7 @@ class FrameListContent extends Component { selectedFrame: PropTypes.object, selectFrame: PropTypes.func.isRequired, columns: PropTypes.object.isRequired, + channelId: PropTypes.number, }; } @@ -63,21 +64,50 @@ class FrameListContent extends Component { }; this.pinnedToBottom = false; this.initIntersectionObserver = false; + this.intersectionObserver = null; } - componentDidUpdate() { + componentDidMount() { const { startPanelContainer } = this.props; const scrollAnchor = this.refs.scrollAnchor; + if (scrollAnchor) { + // Always scroll to anchor when FrameListContent component first mounts. + scrollAnchor.scrollIntoView(); + } + this.setupScrollToBottom(startPanelContainer, scrollAnchor); + } + + componentDidUpdate(prevProps) { + const { startPanelContainer, channelId } = this.props; + const scrollAnchor = this.refs.scrollAnchor; + // When frames are cleared, the previous scrollAnchor would be destroyed, so we need to reset this boolean. if (!scrollAnchor) { this.initIntersectionObserver = false; } + // If a new WebSocket connection is selected, scroll to anchor. + if (channelId !== prevProps.channelId && scrollAnchor) { + scrollAnchor.scrollIntoView(); + } + + this.setupScrollToBottom(startPanelContainer, scrollAnchor); + } + + componentWillUnmount() { + // Reset observables and boolean values. + const scrollAnchor = this.refs.scrollAnchor; + this.intersectionObserver.unobserve(scrollAnchor); + this.initIntersectionObserver = false; + this.pinnedToBottom = false; + } + + setupScrollToBottom(startPanelContainer, scrollAnchor) { if (startPanelContainer && scrollAnchor) { // Initialize intersection observer. if (!this.initIntersectionObserver) { - const observer = new IntersectionObserver( + this.intersectionObserver = new IntersectionObserver( () => { // When scrollAnchor first comes into view, this.pinnedToBottom is set to true. // When the anchor goes out of view, this callback function triggers again and toggles this.pinnedToBottom. @@ -89,7 +119,7 @@ class FrameListContent extends Component { threshold: 0.1, } ); - observer.observe(scrollAnchor); + this.intersectionObserver.observe(scrollAnchor); this.initIntersectionObserver = true; } diff --git a/devtools/client/netmonitor/src/components/websockets/WebSocketsPanel.js b/devtools/client/netmonitor/src/components/websockets/WebSocketsPanel.js index 8f4e7dd0df15..cd4e2e4ab701 100644 --- a/devtools/client/netmonitor/src/components/websockets/WebSocketsPanel.js +++ b/devtools/client/netmonitor/src/components/websockets/WebSocketsPanel.js @@ -107,7 +107,12 @@ class WebSocketsPanel extends Component { } render() { - const { frameDetailsOpen, connector, selectedFrame } = this.props; + const { + frameDetailsOpen, + connector, + selectedFrame, + channelId, + } = this.props; const searchboxRef = this.searchboxRef; const startPanelContainer = this.state.startPanelContainer; @@ -131,6 +136,7 @@ class WebSocketsPanel extends Component { startPanel: FrameListContent({ connector, startPanelContainer, + channelId, }), endPanel: frameDetailsOpen &&